]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'patch-1' of git://github.com/XVilka/BookStack into XVilka-patch-1
authorDan Brown <redacted>
Sat, 20 Apr 2019 12:30:50 +0000 (13:30 +0100)
committerDan Brown <redacted>
Sat, 20 Apr 2019 12:30:50 +0000 (13:30 +0100)
255 files changed:
app/Actions/ActivityService.php
app/Actions/ViewService.php
app/Auth/Access/LdapService.php
app/Auth/Permissions/PermissionService.php
app/Auth/Role.php
app/Auth/UserRepo.php
app/Entities/Book.php
app/Entities/Bookshelf.php
app/Entities/BreadcrumbsViewComposer.php [new file with mode: 0644]
app/Entities/Chapter.php
app/Entities/Entity.php
app/Entities/EntityContextManager.php [new file with mode: 0644]
app/Entities/EntityProvider.php
app/Entities/Page.php
app/Entities/PageRevision.php
app/Entities/Repos/EntityRepo.php
app/Entities/Repos/PageRepo.php
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/BookController.php
app/Http/Controllers/BookshelfController.php
app/Http/Controllers/ChapterController.php
app/Http/Controllers/CommentController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/HomeController.php
app/Http/Controllers/ImageController.php
app/Http/Controllers/PageController.php
app/Http/Controllers/PermissionController.php
app/Http/Controllers/SearchController.php
app/Http/Controllers/SettingController.php
app/Http/Controllers/UserController.php
app/Http/Middleware/Authenticate.php
app/Providers/AppServiceProvider.php
app/Providers/CustomFacadeProvider.php
app/Settings/SettingService.php
app/Uploads/AttachmentService.php
app/helpers.php
config/app.php
package-lock.json
package.json
readme.md
resources/assets/icons/add.svg
resources/assets/icons/books.svg [new file with mode: 0644]
resources/assets/icons/bookshelf.svg
resources/assets/icons/check.svg [new file with mode: 0644]
resources/assets/icons/chevron-right.svg [new file with mode: 0644]
resources/assets/icons/sort-down.svg [new file with mode: 0644]
resources/assets/icons/sort-up.svg [new file with mode: 0644]
resources/assets/js/components/breadcrumb-listing.js [new file with mode: 0644]
resources/assets/js/components/dropdown.js
resources/assets/js/components/entity-selector.js
resources/assets/js/components/expand-toggle.js
resources/assets/js/components/header-mobile-toggle.js [new file with mode: 0644]
resources/assets/js/components/index.js
resources/assets/js/components/list-sort-control.js [new file with mode: 0644]
resources/assets/js/components/markdown-editor.js
resources/assets/js/components/overlay.js
resources/assets/js/components/page-comments.js
resources/assets/js/components/page-display.js
resources/assets/js/components/permissions-table.js [new file with mode: 0644]
resources/assets/js/components/toggle-switch.js
resources/assets/js/components/tri-layout.js [new file with mode: 0644]
resources/assets/js/components/wysiwyg-editor.js
resources/assets/sass/_animations.scss
resources/assets/sass/_blocks.scss
resources/assets/sass/_buttons.scss
resources/assets/sass/_colors.scss [new file with mode: 0644]
resources/assets/sass/_components.scss
resources/assets/sass/_forms.scss
resources/assets/sass/_grid.scss [deleted file]
resources/assets/sass/_header.scss
resources/assets/sass/_html.scss
resources/assets/sass/_layout.scss [new file with mode: 0644]
resources/assets/sass/_lists.scss
resources/assets/sass/_mixins.scss
resources/assets/sass/_pages.scss
resources/assets/sass/_spacing.scss [new file with mode: 0644]
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/print-styles.scss
resources/assets/sass/styles.scss
resources/lang/cs/activities.php [new file with mode: 0644]
resources/lang/cs/auth.php [new file with mode: 0644]
resources/lang/cs/common.php [new file with mode: 0644]
resources/lang/cs/components.php [new file with mode: 0644]
resources/lang/cs/entities.php [new file with mode: 0644]
resources/lang/cs/errors.php [new file with mode: 0644]
resources/lang/cs/pagination.php [new file with mode: 0644]
resources/lang/cs/passwords.php [new file with mode: 0644]
resources/lang/cs/settings.php [new file with mode: 0644]
resources/lang/cs/validation.php [new file with mode: 0644]
resources/lang/en/common.php
resources/lang/en/entities.php
resources/lang/en/settings.php
resources/lang/en/validation.php
resources/lang/es_AR/errors.php
resources/lang/es_AR/validation.php
resources/lang/ru/entities.php
resources/views/auth/forms/login/ldap.blade.php
resources/views/auth/forms/login/standard.blade.php
resources/views/auth/login.blade.php
resources/views/auth/passwords/email.blade.php
resources/views/auth/passwords/reset.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/export.blade.php
resources/views/books/form.blade.php
resources/views/books/grid-item.blade.php
resources/views/books/index.blade.php
resources/views/books/list-item.blade.php
resources/views/books/list.blade.php
resources/views/books/permissions.blade.php [new file with mode: 0644]
resources/views/books/restrictions.blade.php [deleted file]
resources/views/books/show.blade.php
resources/views/books/sort-box.blade.php
resources/views/books/sort.blade.php
resources/views/books/view-toggle.blade.php [deleted file]
resources/views/chapters/_breadcrumbs.blade.php [deleted file]
resources/views/chapters/child-menu.blade.php [new file with mode: 0644]
resources/views/chapters/create.blade.php
resources/views/chapters/delete.blade.php
resources/views/chapters/edit.blade.php
resources/views/chapters/export.blade.php
resources/views/chapters/form.blade.php
resources/views/chapters/list-item.blade.php
resources/views/chapters/move.blade.php
resources/views/chapters/permissions.blade.php [new file with mode: 0644]
resources/views/chapters/restrictions.blade.php [deleted file]
resources/views/chapters/show.blade.php
resources/views/comments/comment.blade.php
resources/views/comments/comments.blade.php
resources/views/comments/create.blade.php [new file with mode: 0644]
resources/views/common/header.blade.php [new file with mode: 0644]
resources/views/common/home-book.blade.php
resources/views/common/home-custom.blade.php
resources/views/common/home-shelves.blade.php
resources/views/common/home-sidebar.blade.php
resources/views/common/home.blade.php
resources/views/components/code-editor.blade.php
resources/views/components/custom-checkbox.blade.php [new file with mode: 0644]
resources/views/components/entity-selector-popup.blade.php
resources/views/components/entity-selector.blade.php
resources/views/components/expand-toggle.blade.php [new file with mode: 0644]
resources/views/components/image-manager.blade.php
resources/views/components/image-picker.blade.php
resources/views/components/tag-manager.blade.php
resources/views/components/toggle-switch.blade.php
resources/views/errors/404.blade.php
resources/views/errors/500.blade.php
resources/views/errors/503.blade.php
resources/views/form/checkbox.blade.php
resources/views/form/delete-button.blade.php [deleted file]
resources/views/form/entity-permissions.blade.php [new file with mode: 0644]
resources/views/form/password.blade.php
resources/views/form/restriction-checkbox.blade.php
resources/views/form/restriction-form.blade.php [deleted file]
resources/views/form/role-checkboxes.blade.php
resources/views/form/role-select.blade.php
resources/views/form/text.blade.php
resources/views/form/textarea.blade.php
resources/views/pages/_breadcrumbs.blade.php [deleted file]
resources/views/pages/copy.blade.php
resources/views/pages/delete.blade.php
resources/views/pages/detailed-listing.blade.php
resources/views/pages/edit.blade.php
resources/views/pages/export.blade.php
resources/views/pages/form-toolbox.blade.php
resources/views/pages/form.blade.php
resources/views/pages/guest-create.blade.php
resources/views/pages/list-item.blade.php
resources/views/pages/move.blade.php
resources/views/pages/permissions.blade.php [new file with mode: 0644]
resources/views/pages/restrictions.blade.php [deleted file]
resources/views/pages/revision.blade.php
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/partials/_header-dropdown.blade.php [deleted file]
resources/views/partials/activity-item.blade.php
resources/views/partials/activity-list.blade.php
resources/views/partials/book-tree.blade.php
resources/views/partials/breadcrumb-listing.blade.php [new file with mode: 0644]
resources/views/partials/breadcrumbs.blade.php [new file with mode: 0644]
resources/views/partials/custom-styles.blade.php
resources/views/partials/entity-dashboard-search-box.blade.php [new file with mode: 0644]
resources/views/partials/entity-dashboard-search-results.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-basic.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-item-basic.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-item.blade.php [new file with mode: 0644]
resources/views/partials/entity-list.blade.php
resources/views/partials/sort.blade.php [new file with mode: 0644]
resources/views/partials/view-toggle.blade.php [new file with mode: 0644]
resources/views/public.blade.php [deleted file]
resources/views/search/all.blade.php
resources/views/search/book.blade.php
resources/views/search/entity-ajax-list.blade.php
resources/views/search/entity-search-list.blade.php [deleted file]
resources/views/settings/index.blade.php
resources/views/settings/maintenance.blade.php
resources/views/settings/navbar.blade.php
resources/views/settings/roles/checkbox.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/shelves/create.blade.php
resources/views/shelves/delete.blade.php
resources/views/shelves/edit.blade.php
resources/views/shelves/export.blade.php [deleted file]
resources/views/shelves/form.blade.php
resources/views/shelves/grid-item.blade.php
resources/views/shelves/index.blade.php
resources/views/shelves/list-item.blade.php
resources/views/shelves/list.blade.php
resources/views/shelves/permissions.blade.php [new file with mode: 0644]
resources/views/shelves/restrictions.blade.php [deleted file]
resources/views/shelves/show.blade.php
resources/views/shelves/view-toggle.blade.php [deleted file]
resources/views/sidebar-layout.blade.php [deleted file]
resources/views/simple-layout.blade.php
resources/views/tri-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/form.blade.php [new file with mode: 0644]
resources/views/users/forms/ldap.blade.php [deleted file]
resources/views/users/forms/standard.blade.php [deleted file]
resources/views/users/forms/system.blade.php [deleted file]
resources/views/users/index.blade.php
resources/views/users/profile.blade.php
routes/web.php
tests/Auth/LdapTest.php
tests/Entity/BookShelfTest.php
tests/Entity/EntitySearchTest.php
tests/Entity/EntityTest.php
tests/Entity/PageContentTest.php
tests/Entity/PageDraftTest.php
tests/Entity/PageRevisionTest.php
tests/HomepageTest.php
tests/Permissions/RestrictionsTest.php
tests/Permissions/RolesTest.php
tests/Uploads/AttachmentTest.php
tests/Uploads/ImageTest.php
tests/UserPreferencesTest.php [new file with mode: 0644]
tests/UserProfileTest.php
tests/test-data/bad.phtml.png [new file with mode: 0644]
webpack.config.js

index 37cd0a6a4ca99a2a1f60f682929fefabe5bfb304..11f8b87328ceec432c4f85b62bc6872edae80118 100644 (file)
@@ -103,7 +103,7 @@ class ActivityService
      * @param int $page
      * @return array
      */
-    public function entityActivity($entity, $count = 20, $page = 0)
+    public function entityActivity($entity, $count = 20, $page = 1)
     {
         if ($entity->isA('book')) {
             $query = $this->activity->where('book_id', '=', $entity->id);
@@ -114,7 +114,7 @@ class ActivityService
         
         $activity = $this->permissionService
             ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
-            ->orderBy('created_at', 'desc')->with(['entity', 'user.avatar'])->skip($count * $page)->take($count)->get();
+            ->orderBy('created_at', 'desc')->with(['entity', 'user.avatar'])->skip($count * ($page - 1))->take($count)->get();
 
         return $this->filterSimilar($activity);
     }
index d5f8002fc04fffe84ef7df57109bc83a4e474a63..532f31c423ca06ae026bb931f03deb84e16bfc87 100644 (file)
@@ -2,21 +2,26 @@
 
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Entities\Entity;
+use BookStack\Entities\EntityProvider;
+use Illuminate\Support\Collection;
 
 class ViewService
 {
     protected $view;
     protected $permissionService;
+    protected $entityProvider;
 
     /**
      * ViewService constructor.
      * @param \BookStack\Actions\View $view
      * @param \BookStack\Auth\Permissions\PermissionService $permissionService
+     * @param EntityProvider $entityProvider
      */
-    public function __construct(View $view, PermissionService $permissionService)
+    public function __construct(View $view, PermissionService $permissionService, EntityProvider $entityProvider)
     {
         $this->view = $view;
         $this->permissionService = $permissionService;
+        $this->entityProvider = $entityProvider;
     }
 
     /**
@@ -50,23 +55,21 @@ class ViewService
      * Get the entities with the most views.
      * @param int $count
      * @param int $page
-     * @param Entity|false|array $filterModel
+     * @param string|array $filterModels
      * @param string $action - used for permission checking
-     * @return
+     * @return Collection
      */
-    public function getPopular($count = 10, $page = 0, $filterModel = false, $action = 'view')
+    public function getPopular(int $count = 10, int $page = 0, $filterModels = null, string $action = 'view')
     {
-        // TODO - Standardise input filter
         $skipCount = $count * $page;
-        $query = $this->permissionService->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type', $action)
+        $query = $this->permissionService
+            ->filterRestrictedEntityRelations($this->view, 'views', 'viewable_id', 'viewable_type', $action)
             ->select('*', 'viewable_id', 'viewable_type', \DB::raw('SUM(views) as view_count'))
             ->groupBy('viewable_id', 'viewable_type')
             ->orderBy('view_count', 'desc');
 
-        if ($filterModel && is_array($filterModel)) {
-            $query->whereIn('viewable_type', $filterModel);
-        } else if ($filterModel) {
-            $query->where('viewable_type', '=', $filterModel->getMorphClass());
+        if ($filterModels) {
+            $query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
         }
 
         return $query->with('viewable')->skip($skipCount)->take($count)->get()->pluck('viewable');
index 9ffbbfbb75b57862c5543c4e4002013bd6485d28..c7415e1f738e723bd18e98ea9a3f5c6a217bb697 100644 (file)
@@ -91,7 +91,7 @@ class LdapService
         $userCn = $this->getUserResponseProperty($user, 'cn', null);
         return [
             'uid'   => $this->getUserResponseProperty($user, 'uid', $user['dn']),
-            'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
+            'name'  => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
             'dn'    => $user['dn'],
             'email' => $this->getUserResponseProperty($user, $emailAttr, null),
         ];
@@ -116,8 +116,8 @@ class LdapService
 
     /**
      * @param Authenticatable $user
-     * @param string          $username
-     * @param string          $password
+     * @param string $username
+     * @param string $password
      * @return bool
      * @throws LdapException
      */
@@ -182,25 +182,14 @@ class LdapService
             throw new LdapException(trans('errors.ldap_extension_not_installed'));
         }
 
-        // Get port from server string and protocol if specified.
-        $ldapServer = explode(':', $this->config['server']);
-        $hasProtocol = preg_match('/^ldaps{0,1}\:\/\//', $this->config['server']) === 1;
-        if (!$hasProtocol) {
-            array_unshift($ldapServer, '');
-        }
-        $hostName = $ldapServer[0] . ($hasProtocol?':':'') . $ldapServer[1];
-        $defaultPort = $ldapServer[0] === 'ldaps' ? 636 : 389;
-
-        /*
-         * Check if TLS_INSECURE is set. The handle is set to NULL due to the nature of
-         * the LDAP_OPT_X_TLS_REQUIRE_CERT option. It can only be set globally and not
-         * per handle.
-         */
+         // Check if TLS_INSECURE is set. The handle is set to NULL due to the nature of
+         // the LDAP_OPT_X_TLS_REQUIRE_CERT option. It can only be set globally and not per handle.
         if ($this->config['tls_insecure']) {
             $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
         }
 
-        $ldapConnection = $this->ldap->connect($hostName, count($ldapServer) > 2 ? intval($ldapServer[2]) : $defaultPort);
+        $serverDetails = $this->parseServerString($this->config['server']);
+        $ldapConnection = $this->ldap->connect($serverDetails['host'], $serverDetails['port']);
 
         if ($ldapConnection === false) {
             throw new LdapException(trans('errors.ldap_cannot_connect'));
@@ -215,6 +204,27 @@ class LdapService
         return $this->ldapConnection;
     }
 
+    /**
+     * Parse a LDAP server string and return the host and port for
+     * a connection. Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'
+     * @param $serverString
+     * @return array
+     */
+    protected function parseServerString($serverString)
+    {
+        $serverNameParts = explode(':', $serverString);
+
+        // If we have a protocol just return the full string since PHP will ignore a separate port.
+        if ($serverNameParts[0] === 'ldaps' || $serverNameParts[0] === 'ldap') {
+            return ['host' => $serverString, 'port' => 389];
+        }
+
+        // Otherwise, extract the port out
+        $hostName = $serverNameParts[0];
+        $ldapPort = (count($serverNameParts) > 1) ? intval($serverNameParts[1]) : 389;
+        return ['host' => $hostName, 'port' => $ldapPort];
+    }
+
     /**
      * Build a filter string by injecting common variables.
      * @param string $filterString
@@ -319,10 +329,10 @@ class LdapService
         $count = 0;
 
         if (isset($userGroupSearchResponse[$groupsAttr]['count'])) {
-            $count = (int) $userGroupSearchResponse[$groupsAttr]['count'];
+            $count = (int)$userGroupSearchResponse[$groupsAttr]['count'];
         }
 
-        for ($i=0; $i<$count; $i++) {
+        for ($i = 0; $i < $count; $i++) {
             $dnComponents = $this->ldap->explodeDn($userGroupSearchResponse[$groupsAttr][$i], 1);
             if (!in_array($dnComponents[0], $ldapGroups)) {
                 $ldapGroups[] = $dnComponents[0];
index 8fc70e916dbd5dec3c5e878222de11861dfd9b45..7e710edaf4be8f8cdb1c6d58ddb83fbc4e9f67f1 100644 (file)
@@ -577,7 +577,7 @@ class PermissionService
                         $query2->where('has_permission_own', '=', 1)
                             ->where('created_by', '=', $userId);
                     });
-        }) ;
+            });
 
         if (!is_null($entityClass)) {
             $entityInstance = app()->make($entityClass);
@@ -704,7 +704,7 @@ class PermissionService
      * @param string $entityIdColumn
      * @param string $entityTypeColumn
      * @param string $action
-     * @return mixed
+     * @return QueryBuilder
      */
     public function filterRestrictedEntityRelations($query, $tableName, $entityIdColumn, $entityTypeColumn, $action = 'view')
     {
index c8bb47e4582076ef90769a03a7f72b8e1e605ca4..917d8aa26192c0388fac58fa5970267f3e25969c 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Auth;
 
 use BookStack\Auth\Permissions\JointPermission;
+use BookStack\Auth\Permissions\RolePermission;
 use BookStack\Model;
 
 class Role extends Model
@@ -13,7 +14,7 @@ class Role extends Model
      */
     public function users()
     {
-        return $this->belongsToMany(User::class);
+        return $this->belongsToMany(User::class)->orderBy('name', 'asc');
     }
 
     /**
@@ -30,7 +31,7 @@ class Role extends Model
      */
     public function permissions()
     {
-        return $this->belongsToMany(Permissions\RolePermission::class, 'permission_role', 'role_id', 'permission_id');
+        return $this->belongsToMany(RolePermission::class, 'permission_role', 'role_id', 'permission_id');
     }
 
     /**
@@ -51,18 +52,18 @@ class Role extends Model
 
     /**
      * Add a permission to this role.
-     * @param \BookStack\Auth\Permissions\RolePermission $permission
+     * @param RolePermission $permission
      */
-    public function attachPermission(Permissions\RolePermission $permission)
+    public function attachPermission(RolePermission $permission)
     {
         $this->permissions()->attach($permission->id);
     }
 
     /**
      * Detach a single permission from this role.
-     * @param \BookStack\Auth\Permissions\RolePermission $permission
+     * @param RolePermission $permission
      */
-    public function detachPermission(Permissions\RolePermission $permission)
+    public function detachPermission(RolePermission $permission)
     {
         $this->permissions()->detach($permission->id);
     }
index 31b91108d2b11e1e28dbb233f4f249dc3092ba38..1a73d0072fb8a51fb5059d98d4943f62205d0fa7 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\UserUpdateException;
 use BookStack\Uploads\Image;
 use Exception;
+use Illuminate\Database\Eloquent\Builder;
 use Images;
 
 class UserRepo
@@ -48,7 +49,7 @@ class UserRepo
 
     /**
      * Get all the users with their permissions.
-     * @return \Illuminate\Database\Eloquent\Builder|static
+     * @return Builder|static
      */
     public function getAllUsers()
     {
@@ -59,7 +60,7 @@ class UserRepo
      * Get all the users with their permissions in a paginated format.
      * @param int $count
      * @param $sortData
-     * @return \Illuminate\Database\Eloquent\Builder|static
+     * @return Builder|static
      */
     public function getAllUsersPaginatedAndSorted($count, $sortData)
     {
@@ -223,16 +224,15 @@ class UserRepo
      */
     public function getRecentlyCreated(User $user, $count = 20)
     {
+        $createdByUserQuery = function(Builder $query) use ($user) {
+            $query->where('created_by', '=', $user->id);
+        };
+
         return [
-            'pages'    => $this->entityRepo->getRecentlyCreated('page', $count, 0, function ($query) use ($user) {
-                $query->where('created_by', '=', $user->id);
-            }),
-            'chapters' => $this->entityRepo->getRecentlyCreated('chapter', $count, 0, function ($query) use ($user) {
-                $query->where('created_by', '=', $user->id);
-            }),
-            'books'    => $this->entityRepo->getRecentlyCreated('book', $count, 0, function ($query) use ($user) {
-                $query->where('created_by', '=', $user->id);
-            })
+            'pages'    => $this->entityRepo->getRecentlyCreated('page', $count, 0, $createdByUserQuery),
+            'chapters' => $this->entityRepo->getRecentlyCreated('chapter', $count, 0, $createdByUserQuery),
+            'books'    => $this->entityRepo->getRecentlyCreated('book', $count, 0, $createdByUserQuery),
+            'shelves'  => $this->entityRepo->getRecentlyCreated('bookshelf', $count, 0, $createdByUserQuery)
         ];
     }
 
@@ -247,6 +247,7 @@ class UserRepo
             'pages'    => $this->entityRepo->getUserTotalCreated('page', $user),
             'chapters' => $this->entityRepo->getUserTotalCreated('chapter', $user),
             'books'    => $this->entityRepo->getUserTotalCreated('book', $user),
+            'shelves'    => $this->entityRepo->getUserTotalCreated('bookshelf', $user),
         ];
     }
 
@@ -256,7 +257,7 @@ class UserRepo
      */
     public function getAllRoles()
     {
-        return $this->role->all();
+        return $this->role->newQuery()->orderBy('name', 'asc')->get();
     }
 
     /**
index 5ab5142c9f097d5c654f5d99e08b8d2efad2d113..77cacf632e03dad04c6b9dcec98843170e046224 100644 (file)
@@ -38,7 +38,7 @@ class Book extends Entity
      */
     public function getBookCover($width = 440, $height = 250)
     {
-        $default = baseUrl('/book_default_cover.png');
+        $default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
         if (!$this->image_id) {
             return $default;
         }
@@ -69,6 +69,15 @@ class Book extends Entity
         return $this->hasMany(Page::class);
     }
 
+    /**
+     * Get the direct child pages of this book.
+     * @return \Illuminate\Database\Eloquent\Relations\HasMany
+     */
+    public function directPages()
+    {
+        return $this->pages()->where('chapter_id', '=', '0');
+    }
+
     /**
      * Get all chapters within this book.
      * @return \Illuminate\Database\Eloquent\Relations\HasMany
@@ -92,7 +101,7 @@ class Book extends Entity
      * @param int $length
      * @return string
      */
-    public function getExcerpt($length = 100)
+    public function getExcerpt(int $length = 100)
     {
         $description = $this->description;
         return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
index c37e36b59f8aefb125c827b3fbdff227708c9314..1de767feca132a283803e04470b8936fa9be3ffe 100644 (file)
@@ -26,7 +26,9 @@ class Bookshelf extends Entity
      */
     public function books()
     {
-        return $this->belongsToMany(Book::class, 'bookshelves_books', 'bookshelf_id', 'book_id')->orderBy('order', 'asc');
+        return $this->belongsToMany(Book::class, 'bookshelves_books', 'bookshelf_id', 'book_id')
+            ->withPivot('order')
+            ->orderBy('order', 'asc');
     }
 
     /**
@@ -50,7 +52,8 @@ class Bookshelf extends Entity
      */
     public function getBookCover($width = 440, $height = 250)
     {
-        $default = baseUrl('/book_default_cover.png');
+        // TODO - Make generic, focused on books right now, Perhaps set-up a better image
+        $default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
         if (!$this->image_id) {
             return $default;
         }
@@ -64,7 +67,7 @@ class Bookshelf extends Entity
     }
 
     /**
-     * Get the cover image of the book
+     * Get the cover image of the shelf
      * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
     public function cover()
@@ -77,7 +80,7 @@ class Bookshelf extends Entity
      * @param int $length
      * @return string
      */
-    public function getExcerpt($length = 100)
+    public function getExcerpt(int $length = 100)
     {
         $description = $this->description;
         return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
@@ -91,4 +94,14 @@ class Bookshelf extends Entity
     {
         return "'BookStack\\\\BookShelf' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text,'' as html, '0' as book_id, '0' as priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at";
     }
+
+    /**
+     * Check if this shelf contains the given book.
+     * @param Book $book
+     * @return bool
+     */
+    public function contains(Book $book)
+    {
+        return $this->books()->where('id', '=', $book->id)->count() > 0;
+    }
 }
diff --git a/app/Entities/BreadcrumbsViewComposer.php b/app/Entities/BreadcrumbsViewComposer.php
new file mode 100644 (file)
index 0000000..7745d9a
--- /dev/null
@@ -0,0 +1,34 @@
+<?php namespace BookStack\Entities;
+
+use Illuminate\View\View;
+
+class BreadcrumbsViewComposer
+{
+
+    protected $entityContextManager;
+
+    /**
+     * BreadcrumbsViewComposer constructor.
+     * @param EntityContextManager $entityContextManager
+     */
+    public function __construct(EntityContextManager $entityContextManager)
+    {
+        $this->entityContextManager = $entityContextManager;
+    }
+
+    /**
+     * Modify data when the view is composed.
+     * @param View $view
+     */
+    public function compose(View $view)
+    {
+        $crumbs = $view->getData()['crumbs'];
+        if (array_first($crumbs) instanceof Book) {
+            $shelf = $this->entityContextManager->getContextualShelfForBook(array_first($crumbs));
+            if ($shelf) {
+                array_unshift($crumbs, $shelf);
+                $view->with('crumbs', $crumbs);
+            }
+        }
+    }
+}
\ No newline at end of file
index 079105ba92a7a5c0d18bdc4ac31085610c8d4ae2..bdacb7c9d8340ee26a43c9652587594d33afadb6 100644 (file)
@@ -53,9 +53,9 @@ class Chapter extends Entity
      * @param int $length
      * @return string
      */
-    public function getExcerpt($length = 100)
+    public function getExcerpt(int $length = 100)
     {
-        $description = $this->description;
+        $description = $this->text ?? $this->description;
         return strlen($description) > $length ? substr($description, 0, $length-3) . '...' : $description;
     }
 
@@ -67,4 +67,13 @@ class Chapter extends Entity
     {
         return "'BookStack\\\\Chapter' as entity_type, id, id as entity_id, slug, name, {$this->textField} as text, '' as html, book_id, priority, '0' as chapter_id, '0' as draft, created_by, updated_by, updated_at, created_at";
     }
+
+    /**
+     * Check if this chapter has any child pages.
+     * @return bool
+     */
+    public function hasChildren()
+    {
+        return count($this->pages) > 0;
+    }
 }
index 21d172e708ad48be7a969a5977c3c4897a9cee33..482d217662ae20b54f62578d88367b8daacb1098 100644 (file)
@@ -102,6 +102,11 @@ class Entity extends Ownable
         return $this->morphMany(View::class, 'viewable');
     }
 
+    public function viewCountQuery()
+    {
+        return $this->views()->selectRaw('viewable_id, sum(views) as view_count')->groupBy('viewable_id');
+    }
+
     /**
      * Get the Tag models that have been user assigned to this entity.
      * @return \Illuminate\Database\Eloquent\Relations\MorphMany
@@ -218,6 +223,20 @@ class Entity extends Ownable
         return $this->{$this->textField};
     }
 
+    /**
+     * Get an excerpt of this entity's descriptive content to the specified length.
+     * @param int $length
+     * @return mixed
+     */
+    public function getExcerpt(int $length = 100)
+    {
+        $text = $this->getText();
+        if (mb_strlen($text) > $length) {
+            $text = mb_substr($text, 0, $length-3) . '...';
+        }
+        return trim($text);
+    }
+
     /**
      * Return a generalised, common raw query that can be 'unioned' across entities.
      * @return string
diff --git a/app/Entities/EntityContextManager.php b/app/Entities/EntityContextManager.php
new file mode 100644 (file)
index 0000000..23ffed0
--- /dev/null
@@ -0,0 +1,62 @@
+<?php namespace BookStack\Entities;
+
+use BookStack\Entities\Repos\EntityRepo;
+use Illuminate\Session\Store;
+
+class EntityContextManager
+{
+    protected $session;
+    protected $entityRepo;
+
+    protected $KEY_SHELF_CONTEXT_ID = 'context_bookshelf_id';
+
+    /**
+     * EntityContextManager constructor.
+     * @param Store $session
+     * @param EntityRepo $entityRepo
+     */
+    public function __construct(Store $session, EntityRepo $entityRepo)
+    {
+        $this->session = $session;
+        $this->entityRepo = $entityRepo;
+    }
+
+    /**
+     * Get the current bookshelf context for the given book.
+     * @param Book $book
+     * @return Bookshelf|null
+     */
+    public function getContextualShelfForBook(Book $book)
+    {
+        $contextBookshelfId = $this->session->get($this->KEY_SHELF_CONTEXT_ID, null);
+        if (is_int($contextBookshelfId)) {
+
+            /** @var Bookshelf $shelf */
+            $shelf = $this->entityRepo->getById('bookshelf', $contextBookshelfId);
+
+            if ($shelf && $shelf->contains($book)) {
+                return $shelf;
+            }
+
+        }
+        return null;
+    }
+
+    /**
+     * Store the current contextual shelf ID.
+     * @param int $shelfId
+     */
+    public function setShelfContext(int $shelfId)
+    {
+        $this->session->put($this->KEY_SHELF_CONTEXT_ID, $shelfId);
+    }
+
+    /**
+     * Clear the session stored shelf context id.
+     */
+    public function clearShelfContext()
+    {
+        $this->session->forget($this->KEY_SHELF_CONTEXT_ID);
+    }
+
+}
\ No newline at end of file
index 04939a14a411d9b344faad349ba8dd59195273cf..d0d4a7ad6c073a7737fdb0a03ef569fbef2487cb 100644 (file)
@@ -84,4 +84,23 @@ class EntityProvider
         $type = strtolower($type);
         return $this->all()[$type];
     }
+
+    /**
+     * Get the morph classes, as an array, for a single or multiple types.
+     * @param string|array $types
+     * @return array<string>
+     */
+    public function getMorphClasses($types)
+    {
+        if (is_string($types)) {
+            $types = [$types];
+        }
+
+        $morphClasses = [];
+        foreach ($types as $type) {
+            $model = $this->get($type);
+            $morphClasses[] = $model->getMorphClass();
+        }
+        return $morphClasses;
+    }
 }
index ea7df16f4021025b048f91dd4e26f50741310e46..1c2cc5cff69c29daa5385d963bc89ab2ad4612ff 100644 (file)
@@ -102,17 +102,6 @@ class Page extends Entity
         return baseUrl('/books/' . urlencode($bookSlug) . $midText . $idComponent);
     }
 
-    /**
-     * Get an excerpt of this page's content to the specified length.
-     * @param int $length
-     * @return mixed
-     */
-    public function getExcerpt($length = 100)
-    {
-        $text = strlen($this->text) > $length ? substr($this->text, 0, $length-3) . '...' : $this->text;
-        return mb_convert_encoding($text, 'UTF-8');
-    }
-
     /**
      * Return a generalised, common raw query that can be 'unioned' across entities.
      * @param bool $withContent
index d30147bfc52b1789f2faf4ecf89d8e3b50345c09..acdec2802bc9a0c0fd922e86bd887939a9effb44 100644 (file)
@@ -62,4 +62,5 @@ class PageRevision extends Model
     {
         return $type === 'revision';
     }
+
 }
index 576ed70f00bdedb45597da098cf85e5c0c15a902..88ca1bca6d2cce234a83b13456a7c403733e9327 100644 (file)
@@ -15,6 +15,7 @@ use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\NotifyException;
 use BookStack\Uploads\AttachmentService;
 use DOMDocument;
+use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Http\Request;
 use Illuminate\Support\Collection;
 
@@ -179,11 +180,38 @@ class EntityRepo
      * Get all entities in a paginated format
      * @param $type
      * @param int $count
+     * @param string $sort
+     * @param string $order
+     * @param null|callable $queryAddition
      * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
      */
-    public function getAllPaginated($type, $count = 10)
+    public function getAllPaginated($type, int $count = 10, string $sort = 'name', string $order = 'asc', $queryAddition = null)
     {
-        return $this->entityQuery($type)->orderBy('name', 'asc')->paginate($count);
+        $query = $this->entityQuery($type);
+        $query = $this->addSortToQuery($query, $sort, $order);
+        if ($queryAddition) {
+            $queryAddition($query);
+        }
+        return $query->paginate($count);
+    }
+
+    /**
+     * Add sorting operations to an entity query.
+     * @param Builder $query
+     * @param string $sort
+     * @param string $order
+     * @return Builder
+     */
+    protected function addSortToQuery(Builder $query, string $sort = 'name', string $order = 'asc')
+    {
+        $order = ($order === 'asc') ? 'asc' : 'desc';
+        $propertySorts = ['name', 'created_at', 'updated_at'];
+
+        if (in_array($sort, $propertySorts)) {
+            return $query->orderBy($sort, $order);
+        }
+
+        return $query;
     }
 
     /**
@@ -265,15 +293,14 @@ class EntityRepo
 
     /**
      * Get the most popular entities base on all views.
-     * @param string|bool $type
+     * @param string $type
      * @param int $count
      * @param int $page
      * @return mixed
      */
-    public function getPopular($type, $count = 10, $page = 0)
+    public function getPopular(string $type, int $count = 10, int $page = 0)
     {
-        $filter = is_bool($type) ? false : $this->entityProvider->get($type);
-        return $this->viewService->getPopular($count, $page, $filter);
+        return $this->viewService->getPopular($count, $page, $type);
     }
 
     /**
@@ -313,6 +340,18 @@ class EntityRepo
         return $this->permissionService->enforceEntityRestrictions('book', $bookshelf->books())->get();
     }
 
+    /**
+     * Get the direct children of a book.
+     * @param Book $book
+     * @return \Illuminate\Database\Eloquent\Collection
+     */
+    public function getBookDirectChildren(Book $book)
+    {
+        $pages = $this->permissionService->enforceEntityRestrictions('page', $book->directPages())->get();
+        $chapters = $this->permissionService->enforceEntityRestrictions('chapters', $book->chapters())->get();
+        return collect()->concat($pages)->concat($chapters)->sortBy('priority')->sortByDesc('draft');
+    }
+
     /**
      * Get all child objects of a book.
      * Returns a sorted collection of Pages and Chapters.
@@ -538,6 +577,21 @@ class EntityRepo
         $shelf->books()->sync($syncData);
     }
 
+    /**
+     * Append a Book to a BookShelf.
+     * @param Bookshelf $shelf
+     * @param Book $book
+     */
+    public function appendBookToShelf(Bookshelf $shelf, Book $book)
+    {
+        if ($shelf->contains($book)) {
+            return;
+        }
+
+        $maxOrder = $shelf->books()->max('order');
+        $shelf->books()->attach($book->id, ['order' => $maxOrder + 1]);
+    }
+
     /**
      * Change the book that an entity belongs to.
      * @param string $type
@@ -661,6 +715,7 @@ class EntityRepo
             }
 
             $doc = new DOMDocument();
+            libxml_use_internal_errors(true);
             $doc->loadHTML(mb_convert_encoding('<body>'.$matchedPage->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
             $matchingElem = $doc->getElementById($splitInclude[1]);
             if ($matchingElem === null) {
@@ -676,6 +731,7 @@ class EntityRepo
                     $innerContent .= $doc->saveHTML($childNode);
                 }
             }
+            libxml_clear_errors();
             $html = str_replace($matches[0][$index], trim($innerContent), $html);
         }
 
index 148ae8459e088f15f0a55ad483add20acc77004a..aad540adba9a7e4b649acb10a2d163b11a259abc 100644 (file)
@@ -139,6 +139,7 @@ class PageRepo extends EntityRepo
         if ($htmlText == '') {
             return $htmlText;
         }
+
         libxml_use_internal_errors(true);
         $doc = new DOMDocument();
         $doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
index e820154e71be2f85b0d771d54b7ae03627022808..78a8d33c0aed8ae342a5a80d86b4c2015ef02158 100644 (file)
@@ -128,7 +128,7 @@ class LoginController extends Controller
             ]);
         }
 
-        return view('auth/login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
+        return view('auth.login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
     }
 
     /**
index cdcca116ccd13a382e019dbcc424231e944065ad..da4f42bb1066a49481b06c1215b89a84642e8213 100644 (file)
@@ -176,7 +176,7 @@ class RegisterController extends Controller
      */
     public function getRegisterConfirmation()
     {
-        return view('auth/register-confirm');
+        return view('auth.register-confirm');
     }
 
     /**
@@ -204,7 +204,7 @@ class RegisterController extends Controller
      */
     public function showAwaitingConfirmation()
     {
-        return view('auth/user-unconfirmed');
+        return view('auth.user-unconfirmed');
     }
 
     /**
index 44368a9c4fb66afb47dc2ed62b70f7a51719707e..24e0d784d0a87b3b558bb4771584dc6414caeedd 100644 (file)
@@ -3,6 +3,7 @@
 use Activity;
 use BookStack\Auth\UserRepo;
 use BookStack\Entities\Book;
+use BookStack\Entities\EntityContextManager;
 use BookStack\Entities\Repos\EntityRepo;
 use BookStack\Entities\ExportService;
 use Illuminate\Http\Request;
@@ -15,18 +16,25 @@ class BookController extends Controller
     protected $entityRepo;
     protected $userRepo;
     protected $exportService;
+    protected $entityContextManager;
 
     /**
      * BookController constructor.
      * @param EntityRepo $entityRepo
-     * @param \BookStack\Auth\UserRepo $userRepo
-     * @param \BookStack\Entities\ExportService $exportService
+     * @param UserRepo $userRepo
+     * @param ExportService $exportService
+     * @param EntityContextManager $entityContextManager
      */
-    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService)
-    {
+    public function __construct(
+        EntityRepo $entityRepo,
+        UserRepo $userRepo,
+        ExportService $exportService,
+        EntityContextManager $entityContextManager
+    ) {
         $this->entityRepo = $entityRepo;
         $this->userRepo = $userRepo;
         $this->exportService = $exportService;
+        $this->entityContextManager = $entityContextManager;
         parent::__construct();
     }
 
@@ -36,67 +44,114 @@ class BookController extends Controller
      */
     public function index()
     {
-        $books = $this->entityRepo->getAllPaginated('book', 18);
+        $view = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books'));
+        $sort = setting()->getUser($this->currentUser, 'books_sort', 'name');
+        $order = setting()->getUser($this->currentUser, 'books_sort_order', 'asc');
+        $sortOptions = [
+            'name' => trans('common.sort_name'),
+            'created_at' => trans('common.sort_created_at'),
+            'updated_at' => trans('common.sort_updated_at'),
+        ];
+
+        $books = $this->entityRepo->getAllPaginated('book', 18, $sort, $order);
         $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('book', 4, 0) : false;
         $popular = $this->entityRepo->getPopular('book', 4, 0);
         $new = $this->entityRepo->getRecentlyCreated('book', 4, 0);
-        $booksViewType = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books', 'list'));
+
+        $this->entityContextManager->clearShelfContext();
+
         $this->setPageTitle(trans('entities.books'));
-        return view('books/index', [
+        return view('books.index', [
             'books' => $books,
             'recents' => $recents,
             'popular' => $popular,
             'new' => $new,
-            'booksViewType' => $booksViewType
+            'view' => $view,
+            'sort' => $sort,
+            'order' => $order,
+            'sortOptions' => $sortOptions,
         ]);
     }
 
     /**
      * Show the form for creating a new book.
+     * @param string $shelfSlug
      * @return Response
+     * @throws \BookStack\Exceptions\NotFoundException
      */
-    public function create()
+    public function create(string $shelfSlug = null)
     {
+        $bookshelf = null;
+        if ($shelfSlug !== null) {
+            $bookshelf = $this->entityRepo->getBySlug('bookshelf', $shelfSlug);
+            $this->checkOwnablePermission('bookshelf-update', $bookshelf);
+        }
+
         $this->checkPermission('book-create-all');
         $this->setPageTitle(trans('entities.books_create'));
-        return view('books/create');
+        return view('books.create', [
+            'bookshelf' => $bookshelf
+        ]);
     }
 
     /**
      * Store a newly created book in storage.
      *
-     * @param  Request $request
+     * @param Request $request
+     * @param string $shelfSlug
      * @return Response
+     * @throws \BookStack\Exceptions\NotFoundException
      */
-    public function store(Request $request)
+    public function store(Request $request, string $shelfSlug = null)
     {
         $this->checkPermission('book-create-all');
         $this->validate($request, [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000'
         ]);
+
+        $bookshelf = null;
+        if ($shelfSlug !== null) {
+            $bookshelf = $this->entityRepo->getBySlug('bookshelf', $shelfSlug);
+            $this->checkOwnablePermission('bookshelf-update', $bookshelf);
+        }
+
         $book = $this->entityRepo->createFromInput('book', $request->all());
         Activity::add($book, 'book_create', $book->id);
+
+        if ($bookshelf) {
+            $this->entityRepo->appendBookToShelf($bookshelf, $book);
+            Activity::add($bookshelf, 'bookshelf_update');
+        }
+
         return redirect($book->getUrl());
     }
 
     /**
      * Display the specified book.
      * @param $slug
+     * @param Request $request
      * @return Response
+     * @throws \BookStack\Exceptions\NotFoundException
      */
-    public function show($slug)
+    public function show($slug, Request $request)
     {
         $book = $this->entityRepo->getBySlug('book', $slug);
         $this->checkOwnablePermission('book-view', $book);
+
         $bookChildren = $this->entityRepo->getBookChildren($book);
+
         Views::add($book);
+        if ($request->has('shelf')) {
+            $this->entityContextManager->setShelfContext(intval($request->get('shelf')));
+        }
+
         $this->setPageTitle($book->getShortName());
-        return view('books/show', [
+        return view('books.show', [
             'book' => $book,
             'current' => $book,
             'bookChildren' => $bookChildren,
-            'activity' => Activity::entityActivity($book, 20, 0)
+            'activity' => Activity::entityActivity($book, 20, 1)
         ]);
     }
 
@@ -110,7 +165,7 @@ class BookController extends Controller
         $book = $this->entityRepo->getBySlug('book', $slug);
         $this->checkOwnablePermission('book-update', $book);
         $this->setPageTitle(trans('entities.books_edit_named', ['bookName'=>$book->getShortName()]));
-        return view('books/edit', ['book' => $book, 'current' => $book]);
+        return view('books.edit', ['book' => $book, 'current' => $book]);
     }
 
     /**
@@ -142,22 +197,24 @@ class BookController extends Controller
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $this->checkOwnablePermission('book-delete', $book);
         $this->setPageTitle(trans('entities.books_delete_named', ['bookName'=>$book->getShortName()]));
-        return view('books/delete', ['book' => $book, 'current' => $book]);
+        return view('books.delete', ['book' => $book, 'current' => $book]);
     }
 
     /**
      * Shows the view which allows pages to be re-ordered and sorted.
      * @param string $bookSlug
      * @return \Illuminate\View\View
+     * @throws \BookStack\Exceptions\NotFoundException
      */
     public function sort($bookSlug)
     {
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $this->checkOwnablePermission('book-update', $book);
+
         $bookChildren = $this->entityRepo->getBookChildren($book, true);
-        $books = $this->entityRepo->getAll('book', false, 'update');
+
         $this->setPageTitle(trans('entities.books_sort_named', ['bookName'=>$book->getShortName()]));
-        return view('books/sort', ['book' => $book, 'current' => $book, 'books' => $books, 'bookChildren' => $bookChildren]);
+        return view('books.sort', ['book' => $book, 'current' => $book, 'bookChildren' => $bookChildren]);
     }
 
     /**
@@ -170,7 +227,7 @@ class BookController extends Controller
     {
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $bookChildren = $this->entityRepo->getBookChildren($book);
-        return view('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren]);
+        return view('books.sort-box', ['book' => $book, 'bookChildren' => $bookChildren]);
     }
 
     /**
@@ -263,12 +320,12 @@ class BookController extends Controller
      * @param $bookSlug
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function showRestrict($bookSlug)
+    public function showPermissions($bookSlug)
     {
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $book);
         $roles = $this->userRepo->getRestrictableRoles();
-        return view('books/restrictions', [
+        return view('books.permissions', [
             'book' => $book,
             'roles' => $roles
         ]);
@@ -277,11 +334,12 @@ class BookController extends Controller
     /**
      * Set the restrictions for this book.
      * @param $bookSlug
-     * @param $bookSlug
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @throws \BookStack\Exceptions\NotFoundException
+     * @throws \Throwable
      */
-    public function restrict($bookSlug, Request $request)
+    public function permissions($bookSlug, Request $request)
     {
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $book);
index 5c2898786f05d9d81db7e33130c0c6e5856ec2ea..b86bc2e38d97c58228f4ee88eb7f493e125de7cd 100644 (file)
@@ -3,8 +3,8 @@
 use Activity;
 use BookStack\Auth\UserRepo;
 use BookStack\Entities\Bookshelf;
+use BookStack\Entities\EntityContextManager;
 use BookStack\Entities\Repos\EntityRepo;
-use BookStack\Entities\ExportService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
 use Views;
@@ -14,19 +14,19 @@ class BookshelfController extends Controller
 
     protected $entityRepo;
     protected $userRepo;
-    protected $exportService;
+    protected $entityContextManager;
 
     /**
      * BookController constructor.
-     * @param \BookStack\Entities\Repos\EntityRepo $entityRepo
+     * @param EntityRepo $entityRepo
      * @param UserRepo $userRepo
-     * @param \BookStack\Entities\ExportService $exportService
+     * @param EntityContextManager $entityContextManager
      */
-    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, ExportService $exportService)
+    public function __construct(EntityRepo $entityRepo, UserRepo $userRepo, EntityContextManager $entityContextManager)
     {
         $this->entityRepo = $entityRepo;
         $this->userRepo = $userRepo;
-        $this->exportService = $exportService;
+        $this->entityContextManager = $entityContextManager;
         parent::__construct();
     }
 
@@ -36,19 +36,35 @@ class BookshelfController extends Controller
      */
     public function index()
     {
-        $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18);
+        $view = setting()->getUser($this->currentUser, 'bookshelves_view_type', config('app.views.bookshelves', 'grid'));
+        $sort = setting()->getUser($this->currentUser, 'bookshelves_sort', 'name');
+        $order = setting()->getUser($this->currentUser, 'bookshelves_sort_order', 'asc');
+        $sortOptions = [
+            'name' => trans('common.sort_name'),
+            'created_at' => trans('common.sort_created_at'),
+            'updated_at' => trans('common.sort_updated_at'),
+        ];
+
+        $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18, $sort, $order);
+        foreach ($shelves as $shelf) {
+            $shelf->books = $this->entityRepo->getBookshelfChildren($shelf);
+        }
+
         $recents = $this->signedIn ? $this->entityRepo->getRecentlyViewed('bookshelf', 4, 0) : false;
         $popular = $this->entityRepo->getPopular('bookshelf', 4, 0);
         $new = $this->entityRepo->getRecentlyCreated('bookshelf', 4, 0);
-        $shelvesViewType = setting()->getUser($this->currentUser, 'bookshelves_view_type', config('app.views.bookshelves', 'grid'));
 
+        $this->entityContextManager->clearShelfContext();
         $this->setPageTitle(trans('entities.shelves'));
-        return view('shelves/index', [
+        return view('shelves.index', [
             'shelves' => $shelves,
             'recents' => $recents,
             'popular' => $popular,
             'new' => $new,
-            'shelvesViewType' => $shelvesViewType
+            'view' => $view,
+            'sort' => $sort,
+            'order' => $order,
+            'sortOptions' => $sortOptions,
         ]);
     }
 
@@ -61,7 +77,7 @@ class BookshelfController extends Controller
         $this->checkPermission('bookshelf-create-all');
         $books = $this->entityRepo->getAll('book', false, 'update');
         $this->setPageTitle(trans('entities.shelves_create'));
-        return view('shelves/create', ['books' => $books]);
+        return view('shelves.create', ['books' => $books]);
     }
 
     /**
@@ -93,17 +109,19 @@ class BookshelfController extends Controller
      */
     public function show(string $slug)
     {
-        $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug); /** @var $bookshelf Bookshelf */
+        /** @var Bookshelf $bookshelf */
+        $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug);
         $this->checkOwnablePermission('book-view', $bookshelf);
 
         $books = $this->entityRepo->getBookshelfChildren($bookshelf);
         Views::add($bookshelf);
+        $this->entityContextManager->setShelfContext($bookshelf->id);
 
         $this->setPageTitle($bookshelf->getShortName());
-        return view('shelves/show', [
+        return view('shelves.show', [
             'shelf' => $bookshelf,
             'books' => $books,
-            'activity' => Activity::entityActivity($bookshelf, 20, 0)
+            'activity' => Activity::entityActivity($bookshelf, 20, 1)
         ]);
     }
 
@@ -126,7 +144,7 @@ class BookshelfController extends Controller
         });
 
         $this->setPageTitle(trans('entities.shelves_edit_named', ['name' => $bookshelf->getShortName()]));
-        return view('shelves/edit', [
+        return view('shelves.edit', [
             'shelf' => $bookshelf,
             'books' => $books,
             'shelfBooks' => $shelfBooks,
@@ -170,7 +188,7 @@ class BookshelfController extends Controller
         $this->checkOwnablePermission('bookshelf-delete', $bookshelf);
 
         $this->setPageTitle(trans('entities.shelves_delete_named', ['name' => $bookshelf->getShortName()]));
-        return view('shelves/delete', ['shelf' => $bookshelf]);
+        return view('shelves.delete', ['shelf' => $bookshelf]);
     }
 
     /**
@@ -190,31 +208,32 @@ class BookshelfController extends Controller
     }
 
     /**
-     * Show the Restrictions view.
-     * @param $slug
+     * Show the permissions view.
+     * @param string $slug
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      * @throws \BookStack\Exceptions\NotFoundException
      */
-    public function showRestrict(string $slug)
+    public function showPermissions(string $slug)
     {
         $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug);
         $this->checkOwnablePermission('restrictions-manage', $bookshelf);
 
         $roles = $this->userRepo->getRestrictableRoles();
-        return view('shelves.restrictions', [
+        return view('shelves.permissions', [
             'shelf' => $bookshelf,
             'roles' => $roles
         ]);
     }
 
     /**
-     * Set the restrictions for this bookshelf.
-     * @param $slug
+     * Set the permissions for this bookshelf.
+     * @param string $slug
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      * @throws \BookStack\Exceptions\NotFoundException
+     * @throws \Throwable
      */
-    public function restrict(string $slug, Request $request)
+    public function permissions(string $slug, Request $request)
     {
         $bookshelf = $this->entityRepo->getBySlug('bookshelf', $slug);
         $this->checkOwnablePermission('restrictions-manage', $bookshelf);
index 20ab9613318947d962997953d865413f882134cd..c19e45694850f7418ca8f5c98c5ae15a024847f9 100644 (file)
@@ -39,7 +39,7 @@ class ChapterController extends Controller
         $book = $this->entityRepo->getBySlug('book', $bookSlug);
         $this->checkOwnablePermission('chapter-create', $book);
         $this->setPageTitle(trans('entities.chapters_create'));
-        return view('chapters/create', ['book' => $book, 'current' => $book]);
+        return view('chapters.create', ['book' => $book, 'current' => $book]);
     }
 
     /**
@@ -78,7 +78,7 @@ class ChapterController extends Controller
         Views::add($chapter);
         $this->setPageTitle($chapter->getShortName());
         $pages = $this->entityRepo->getChapterChildren($chapter);
-        return view('chapters/show', [
+        return view('chapters.show', [
             'book' => $chapter->book,
             'chapter' => $chapter,
             'current' => $chapter,
@@ -98,7 +98,7 @@ class ChapterController extends Controller
         $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
         $this->checkOwnablePermission('chapter-update', $chapter);
         $this->setPageTitle(trans('entities.chapters_edit_named', ['chapterName' => $chapter->getShortName()]));
-        return view('chapters/edit', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
+        return view('chapters.edit', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
     }
 
     /**
@@ -130,7 +130,7 @@ class ChapterController extends Controller
         $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
         $this->checkOwnablePermission('chapter-delete', $chapter);
         $this->setPageTitle(trans('entities.chapters_delete_named', ['chapterName' => $chapter->getShortName()]));
-        return view('chapters/delete', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
+        return view('chapters.delete', ['book' => $chapter->book, 'chapter' => $chapter, 'current' => $chapter]);
     }
 
     /**
@@ -162,7 +162,7 @@ class ChapterController extends Controller
         $this->setPageTitle(trans('entities.chapters_move_named', ['chapterName' => $chapter->getShortName()]));
         $this->checkOwnablePermission('chapter-update', $chapter);
         $this->checkOwnablePermission('chapter-delete', $chapter);
-        return view('chapters/move', [
+        return view('chapters.move', [
             'chapter' => $chapter,
             'book' => $chapter->book
         ]);
@@ -214,13 +214,14 @@ class ChapterController extends Controller
      * @param $bookSlug
      * @param $chapterSlug
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+     * @throws \BookStack\Exceptions\NotFoundException
      */
-    public function showRestrict($bookSlug, $chapterSlug)
+    public function showPermissions($bookSlug, $chapterSlug)
     {
         $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $chapter);
         $roles = $this->userRepo->getRestrictableRoles();
-        return view('chapters/restrictions', [
+        return view('chapters.permissions', [
             'chapter' => $chapter,
             'roles' => $roles
         ]);
@@ -232,8 +233,10 @@ class ChapterController extends Controller
      * @param $chapterSlug
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @throws \BookStack\Exceptions\NotFoundException
+     * @throws \Throwable
      */
-    public function restrict($bookSlug, $chapterSlug, Request $request)
+    public function permissions($bookSlug, $chapterSlug, Request $request)
     {
         $chapter = $this->entityRepo->getBySlug('chapter', $chapterSlug, $bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $chapter);
index 2039ce7fe9ff749a2e2c76a2eb20b9d96b0701ef..860b507623fb6b5d09cd6a2a1f1a4aea5021fc35 100644 (file)
@@ -54,7 +54,7 @@ class CommentController extends Controller
         $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]);
+        return view('comments.comment', ['comment' => $comment]);
     }
 
     /**
@@ -75,7 +75,7 @@ class CommentController extends Controller
         $this->checkOwnablePermission('comment-update', $comment);
 
         $comment = $this->commentRepo->update($comment, $request->only(['html', 'text']));
-        return view('comments/comment', ['comment' => $comment]);
+        return view('comments.comment', ['comment' => $comment]);
     }
 
     /**
index 80f567eaa80279e8f49599f9703f5ab29e6276c3..fc4f184fcabdff0bf292b6f6c257912ce6d98a2d 100644 (file)
@@ -123,6 +123,20 @@ abstract class Controller extends BaseController
         return true;
     }
 
+    /**
+     * Check if the current user has a permission or bypass if the provided user
+     * id matches the current user.
+     * @param string $permissionName
+     * @param int $userId
+     * @return bool
+     */
+    protected function checkPermissionOrCurrentUser(string $permissionName, int $userId)
+    {
+        return $this->checkPermissionOr($permissionName, function() use ($userId) {
+            return $userId === $this->currentUser->id;
+        });
+    }
+
     /**
      * Send back a json error message.
      * @param string $messageText
index 5a5f34e4a5ce0d61e37bc797cfe1350bb49a5ef0..c5f3cd02a4bcee24e88eca0110dcc62c7825a41c 100644 (file)
@@ -19,7 +19,6 @@ class HomeController extends Controller
         parent::__construct();
     }
 
-
     /**
      * Display the homepage.
      * @return Response
@@ -45,17 +44,36 @@ class HomeController extends Controller
             'draftPages' => $draftPages,
         ];
 
+        // Add required list ordering & sorting for books & shelves views.
+        if ($homepageOption === 'bookshelves' || $homepageOption === 'books') {
+            $key = $homepageOption;
+            $view = setting()->getUser($this->currentUser, $key . '_view_type', config('app.views.' . $key));
+            $sort = setting()->getUser($this->currentUser, $key . '_sort', 'name');
+            $order = setting()->getUser($this->currentUser, $key . '_sort_order', 'asc');
+
+            $sortOptions = [
+                'name' => trans('common.sort_name'),
+                'created_at' => trans('common.sort_created_at'),
+                'updated_at' => trans('common.sort_updated_at'),
+            ];
+
+            $commonData = array_merge($commonData, [
+                'view' => $view,
+                'sort' => $sort,
+                'order' => $order,
+                'sortOptions' => $sortOptions,
+            ]);
+        }
+
         if ($homepageOption === 'bookshelves') {
-            $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18);
-            $shelvesViewType = setting()->getUser($this->currentUser, 'bookshelves_view_type', config('app.views.bookshelves', 'grid'));
-            $data = array_merge($commonData, ['shelves' => $shelves, 'shelvesViewType' => $shelvesViewType]);
+            $shelves = $this->entityRepo->getAllPaginated('bookshelf', 18, $commonData['sort'], $commonData['order']);
+            $data = array_merge($commonData, ['shelves' => $shelves]);
             return view('common.home-shelves', $data);
         }
 
         if ($homepageOption === 'books') {
-            $books = $this->entityRepo->getAllPaginated('book', 18);
-            $booksViewType = setting()->getUser($this->currentUser, 'books_view_type', config('app.views.books', 'list'));
-            $data = array_merge($commonData, ['books' => $books, 'booksViewType' => $booksViewType]);
+            $books = $this->entityRepo->getAllPaginated('book', 18, $commonData['sort'], $commonData['order']);
+            $data = array_merge($commonData, ['books' => $books]);
             return view('common.home-book', $data);
         }
 
@@ -105,7 +123,7 @@ class HomeController extends Controller
      */
     public function customHeadContent()
     {
-        return view('partials/custom-head-content');
+        return view('partials.custom-head-content');
     }
 
     /**
@@ -120,7 +138,7 @@ class HomeController extends Controller
             $allowRobots = $sitePublic;
         }
         return response()
-            ->view('common/robots', ['allowRobots' => $allowRobots])
+            ->view('common.robots', ['allowRobots' => $allowRobots])
             ->header('Content-Type', 'text/plain');
     }
 
@@ -129,6 +147,6 @@ class HomeController extends Controller
      */
     public function getNotFound()
     {
-        return response()->view('errors/404', [], 404);
+        return response()->view('errors.404', [], 404);
     }
 }
index fdcb3aba49fbd0380cc878c9953b232e26cedf61..4d6f759b336eddb97e0516c6ddb69eb8034c9639 100644 (file)
@@ -119,7 +119,7 @@ class ImageController extends Controller
     {
         $this->checkPermission('image-create-all');
         $this->validate($request, [
-            'file' => 'image_extension|mimes:jpeg,png,gif,bmp,webp,tiff'
+            'file' => 'image_extension|no_double_extension|mimes:jpeg,png,gif,bmp,webp,tiff'
         ]);
 
         if (!$this->imageRepo->isValidType($type)) {
index d95e02470bee10c63d436421ac7a53cdcff6577c..16a7d5a5e45df6df1094bfa14df63fb17cb278f3 100644 (file)
@@ -61,7 +61,7 @@ class PageController extends Controller
 
         // Otherwise show the edit view if they're a guest
         $this->setPageTitle(trans('entities.pages_new'));
-        return view('pages/guest-create', ['parent' => $parent]);
+        return view('pages.guest-create', ['parent' => $parent]);
     }
 
     /**
@@ -110,7 +110,7 @@ class PageController extends Controller
         $this->setPageTitle(trans('entities.pages_edit_draft'));
 
         $draftsEnabled = $this->signedIn;
-        return view('pages/edit', [
+        return view('pages.edit', [
             'page' => $draft,
             'book' => $draft->book,
             'isDraft' => true,
@@ -184,7 +184,7 @@ class PageController extends Controller
 
         Views::add($page);
         $this->setPageTitle($page->getShortName());
-        return view('pages/show', [
+        return view('pages.show', [
             'page' => $page,'book' => $page->book,
             'current' => $page,
             'sidebarTree' => $sidebarTree,
@@ -239,7 +239,7 @@ class PageController extends Controller
         }
 
         $draftsEnabled = $this->signedIn;
-        return view('pages/edit', [
+        return view('pages.edit', [
             'page' => $page,
             'book' => $page->book,
             'current' => $page,
@@ -317,7 +317,7 @@ class PageController extends Controller
         $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
         $this->checkOwnablePermission('page-delete', $page);
         $this->setPageTitle(trans('entities.pages_delete_named', ['pageName'=>$page->getShortName()]));
-        return view('pages/delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
+        return view('pages.delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
     }
 
 
@@ -333,7 +333,7 @@ class PageController extends Controller
         $page = $this->pageRepo->getById('page', $pageId, true);
         $this->checkOwnablePermission('page-update', $page);
         $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName'=>$page->getShortName()]));
-        return view('pages/delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
+        return view('pages.delete', ['book' => $page->book, 'page' => $page, 'current' => $page]);
     }
 
     /**
@@ -377,12 +377,13 @@ class PageController extends Controller
      * @param string $bookSlug
      * @param string $pageSlug
      * @return \Illuminate\View\View
+     * @throws NotFoundException
      */
     public function showRevisions($bookSlug, $pageSlug)
     {
         $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
         $this->setPageTitle(trans('entities.pages_revisions_named', ['pageName'=>$page->getShortName()]));
-        return view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]);
+        return view('pages.revisions', ['page' => $page, 'current' => $page]);
     }
 
     /**
@@ -403,9 +404,10 @@ class PageController extends Controller
         $page->fill($revision->toArray());
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
 
-        return view('pages/revision', [
+        return view('pages.revision', [
             'page' => $page,
             'book' => $page->book,
+            'diff' => null,
             'revision' => $revision
         ]);
     }
@@ -432,7 +434,7 @@ class PageController extends Controller
         $page->fill($revision->toArray());
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
 
-        return view('pages/revision', [
+        return view('pages.revision', [
             'page' => $page,
             'book' => $page->book,
             'diff' => $diff,
@@ -482,12 +484,12 @@ class PageController extends Controller
         // Check if its the latest revision, cannot delete latest revision.
         if (intval($currentRevision->id) === intval($revId)) {
             session()->flash('error', trans('entities.revision_cannot_delete_latest'));
-            return response()->view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page], 400);
+            return response()->view('pages.revisions', ['page' => $page, 'book' => $page->book, 'current' => $page], 400);
         }
 
         $revision->delete();
         session()->flash('success', trans('entities.revision_delete_success'));
-        return view('pages/revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]);
+        return view('pages.revisions', ['page' => $page, 'book' => $page->book, 'current' => $page]);
     }
 
     /**
@@ -532,49 +534,20 @@ class PageController extends Controller
         return $this->downloadResponse($pageText, $pageSlug . '.txt');
     }
 
-    /**
-     * Show a listing of recently created pages
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
-     */
-    public function showRecentlyCreated()
-    {
-        $pages = $this->pageRepo->getRecentlyCreatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-created'));
-        return view('pages/detailed-listing', [
-            'title' => trans('entities.recently_created_pages'),
-            'pages' => $pages
-        ]);
-    }
-
     /**
      * Show a listing of recently created pages
      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
     public function showRecentlyUpdated()
     {
+        // TODO - Still exist?
         $pages = $this->pageRepo->getRecentlyUpdatedPaginated('page', 20)->setPath(baseUrl('/pages/recently-updated'));
-        return view('pages/detailed-listing', [
+        return view('pages.detailed-listing', [
             'title' => trans('entities.recently_updated_pages'),
             'pages' => $pages
         ]);
     }
 
-    /**
-     * Show the Restrictions view.
-     * @param string $bookSlug
-     * @param string $pageSlug
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
-     */
-    public function showRestrict($bookSlug, $pageSlug)
-    {
-        $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
-        $this->checkOwnablePermission('restrictions-manage', $page);
-        $roles = $this->userRepo->getRestrictableRoles();
-        return view('pages/restrictions', [
-            'page'  => $page,
-            'roles' => $roles
-        ]);
-    }
-
     /**
      * Show the view to choose a new parent to move a page into.
      * @param string $bookSlug
@@ -587,7 +560,7 @@ class PageController extends Controller
         $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
         $this->checkOwnablePermission('page-update', $page);
         $this->checkOwnablePermission('page-delete', $page);
-        return view('pages/move', [
+        return view('pages.move', [
             'book' => $page->book,
             'page' => $page
         ]);
@@ -645,7 +618,7 @@ class PageController extends Controller
         $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
         $this->checkOwnablePermission('page-view', $page);
         session()->flashInput(['name' => $page->name]);
-        return view('pages/copy', [
+        return view('pages.copy', [
             'book' => $page->book,
             'page' => $page
         ]);
@@ -690,6 +663,24 @@ class PageController extends Controller
         return redirect($pageCopy->getUrl());
     }
 
+    /**
+     * Show the Permissions view.
+     * @param string $bookSlug
+     * @param string $pageSlug
+     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
+     * @throws NotFoundException
+     */
+    public function showPermissions($bookSlug, $pageSlug)
+    {
+        $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
+        $this->checkOwnablePermission('restrictions-manage', $page);
+        $roles = $this->userRepo->getRestrictableRoles();
+        return view('pages.permissions', [
+            'page'  => $page,
+            'roles' => $roles
+        ]);
+    }
+
     /**
      * Set the permissions for this page.
      * @param string $bookSlug
@@ -697,8 +688,9 @@ class PageController extends Controller
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      * @throws NotFoundException
+     * @throws \Throwable
      */
-    public function restrict($bookSlug, $pageSlug, Request $request)
+    public function permissions($bookSlug, $pageSlug, Request $request)
     {
         $page = $this->pageRepo->getPageBySlug($pageSlug, $bookSlug);
         $this->checkOwnablePermission('restrictions-manage', $page);
index 9be343c9a76011a5bd5b7a872ad78b1e6a9a8d3c..9893d59935ff6d284d98d5e335e2b452089de15e 100644 (file)
@@ -26,7 +26,7 @@ class PermissionController extends Controller
     {
         $this->checkPermission('user-roles-manage');
         $roles = $this->permissionsRepo->getAllRoles();
-        return view('settings/roles/index', ['roles' => $roles]);
+        return view('settings.roles.index', ['roles' => $roles]);
     }
 
     /**
@@ -36,7 +36,7 @@ class PermissionController extends Controller
     public function createRole()
     {
         $this->checkPermission('user-roles-manage');
-        return view('settings/roles/create');
+        return view('settings.roles.create');
     }
 
     /**
@@ -70,7 +70,7 @@ class PermissionController extends Controller
         if ($role->hidden) {
             throw new PermissionsException(trans('errors.role_cannot_be_edited'));
         }
-        return view('settings/roles/edit', ['role' => $role]);
+        return view('settings.roles.edit', ['role' => $role]);
     }
 
     /**
@@ -106,7 +106,7 @@ class PermissionController extends Controller
         $roles = $this->permissionsRepo->getAllRolesExcept($role);
         $blankRole = $role->newInstance(['display_name' => trans('settings.role_delete_no_migration')]);
         $roles->prepend($blankRole);
-        return view('settings/roles/delete', ['role' => $role, 'roles' => $roles]);
+        return view('settings.roles.delete', ['role' => $role, 'roles' => $roles]);
     }
 
     /**
index d8f2dc4d7eac6a1eec6e7f0a584b893f4a32c9a7..4bcf7b40eff4db0c207f7741438a4da9b52bd113 100644 (file)
@@ -1,34 +1,45 @@
 <?php namespace BookStack\Http\Controllers;
 
 use BookStack\Actions\ViewService;
+use BookStack\Entities\EntityContextManager;
 use BookStack\Entities\Repos\EntityRepo;
 use BookStack\Entities\SearchService;
+use BookStack\Exceptions\NotFoundException;
+use Illuminate\Contracts\View\Factory;
 use Illuminate\Http\Request;
+use Illuminate\View\View;
 
 class SearchController extends Controller
 {
     protected $entityRepo;
     protected $viewService;
     protected $searchService;
+    protected $entityContextManager;
 
     /**
      * SearchController constructor.
-     * @param \BookStack\Entities\Repos\EntityRepo $entityRepo
+     * @param EntityRepo $entityRepo
      * @param ViewService $viewService
      * @param SearchService $searchService
+     * @param EntityContextManager $entityContextManager
      */
-    public function __construct(EntityRepo $entityRepo, ViewService $viewService, SearchService $searchService)
-    {
+    public function __construct(
+        EntityRepo $entityRepo,
+        ViewService $viewService,
+        SearchService $searchService,
+        EntityContextManager $entityContextManager
+    ) {
         $this->entityRepo = $entityRepo;
         $this->viewService = $viewService;
         $this->searchService = $searchService;
+        $this->entityContextManager = $entityContextManager;
         parent::__construct();
     }
 
     /**
      * Searches all entities.
      * @param Request $request
-     * @return \Illuminate\View\View
+     * @return View
      * @internal param string $searchTerm
      */
     public function search(Request $request)
@@ -41,7 +52,7 @@ class SearchController extends Controller
 
         $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
 
-        return view('search/all', [
+        return view('search.all', [
             'entities'   => $results['results'],
             'totalResults' => $results['total'],
             'searchTerm' => $searchTerm,
@@ -55,28 +66,28 @@ class SearchController extends Controller
      * Searches all entities within a book.
      * @param Request $request
      * @param integer $bookId
-     * @return \Illuminate\View\View
+     * @return View
      * @internal param string $searchTerm
      */
     public function searchBook(Request $request, $bookId)
     {
         $term = $request->get('term', '');
         $results = $this->searchService->searchBook($bookId, $term);
-        return view('partials/entity-list', ['entities' => $results]);
+        return view('partials.entity-list', ['entities' => $results]);
     }
 
     /**
      * Searches all entities within a chapter.
      * @param Request $request
      * @param integer $chapterId
-     * @return \Illuminate\View\View
+     * @return View
      * @internal param string $searchTerm
      */
     public function searchChapter(Request $request, $chapterId)
     {
         $term = $request->get('term', '');
         $results = $this->searchService->searchChapter($chapterId, $term);
-        return view('partials/entity-list', ['entities' => $results]);
+        return view('partials.entity-list', ['entities' => $results]);
     }
 
     /**
@@ -87,21 +98,64 @@ class SearchController extends Controller
      */
     public function searchEntitiesAjax(Request $request)
     {
-        $entityTypes = $request->filled('types') ? collect(explode(',', $request->get('types'))) : collect(['page', 'chapter', 'book']);
+        $entityTypes = $request->filled('types') ? explode(',', $request->get('types')) : ['page', 'chapter', 'book'];
         $searchTerm =  $request->get('term', false);
         $permission = $request->get('permission', 'view');
 
         // Search for entities otherwise show most popular
         if ($searchTerm !== false) {
-            $searchTerm .= ' {type:'. implode('|', $entityTypes->toArray()) .'}';
+            $searchTerm .= ' {type:'. implode('|', $entityTypes) .'}';
             $entities = $this->searchService->searchEntities($searchTerm, 'all', 1, 20, $permission)['results'];
         } else {
-            $entityNames = $entityTypes->map(function ($type) {
-                return 'BookStack\\' . ucfirst($type); // TODO - Extract this elsewhere, too specific and stringy
-            })->toArray();
-            $entities = $this->viewService->getPopular(20, 0, $entityNames, $permission);
+            $entities = $this->viewService->getPopular(20, 0, $entityTypes, $permission);
+        }
+
+        return view('search.entity-ajax-list', ['entities' => $entities]);
+    }
+
+    /**
+     * Search siblings items in the system.
+     * @param Request $request
+     * @return Factory|View|mixed
+     */
+    public function searchSiblings(Request $request)
+    {
+        $type = $request->get('entity_type', null);
+        $id = $request->get('entity_id', null);
+
+        $entity = $this->entityRepo->getById($type, $id);
+        if (!$entity) {
+            return $this->jsonError(trans('errors.entity_not_found'), 404);
+        }
+
+        $entities = [];
+
+        // Page in chapter
+        if ($entity->isA('page') && $entity->chapter) {
+            $entities = $this->entityRepo->getChapterChildren($entity->chapter);
+        }
+
+        // Page in book or chapter
+        if (($entity->isA('page') && !$entity->chapter) || $entity->isA('chapter')) {
+            $entities = $this->entityRepo->getBookDirectChildren($entity->book);
+        }
+
+        // Book
+        // Gets just the books in a shelf if shelf is in context
+        if ($entity->isA('book')) {
+            $contextShelf = $this->entityContextManager->getContextualShelfForBook($entity);
+            if ($contextShelf) {
+                $entities = $this->entityRepo->getBookshelfChildren($contextShelf);
+            } else {
+                $entities = $this->entityRepo->getAll('book');
+            }
+        }
+
+        // Shelve
+        if ($entity->isA('bookshelf')) {
+            $entities = $this->entityRepo->getAll('bookshelf');
         }
 
-        return view('search/entity-ajax-list', ['entities' => $entities]);
+        return view('partials.entity-list-basic', ['entities' => $entities, 'style' => 'compact']);
     }
 }
index 01fb68fe06e509908bd863671eddf0f2c94ba323..159e19a2bb441d8b8ef3d296ad0ef856471fbe9f 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Auth\User;
 use BookStack\Uploads\ImageService;
 use Illuminate\Http\Request;
 use Illuminate\Http\Response;
@@ -19,7 +20,10 @@ class SettingController extends Controller
         // Get application version
         $version = trim(file_get_contents(base_path('version')));
 
-        return view('settings/index', ['version' => $version]);
+        return view('settings.index', [
+            'version' => $version,
+            'guestUser' => User::getDefault()
+        ]);
     }
 
     /**
@@ -57,7 +61,7 @@ class SettingController extends Controller
         // Get application version
         $version = trim(file_get_contents(base_path('version')));
 
-        return view('settings/maintenance', ['version' => $version]);
+        return view('settings.maintenance', ['version' => $version]);
     }
 
     /**
index cc5ada3f283d2bd4e24f01cc9235e637b0ffa9a1..1bb5d46cdabba8ad19b86aac81060438ec257cfa 100644 (file)
@@ -41,7 +41,7 @@ class UserController extends Controller
         $users = $this->userRepo->getAllUsersPaginatedAndSorted(20, $listDetails);
         $this->setPageTitle(trans('settings.users'));
         $users->appends($listDetails);
-        return view('users/index', ['users' => $users, 'listDetails' => $listDetails]);
+        return view('users.index', ['users' => $users, 'listDetails' => $listDetails]);
     }
 
     /**
@@ -53,7 +53,7 @@ class UserController extends Controller
         $this->checkPermission('users-manage');
         $authMethod = config('auth.method');
         $roles = $this->userRepo->getAllRoles();
-        return view('users/create', ['authMethod' => $authMethod, 'roles' => $roles]);
+        return view('users.create', ['authMethod' => $authMethod, 'roles' => $roles]);
     }
 
     /**
@@ -118,7 +118,7 @@ class UserController extends Controller
         $activeSocialDrivers = $socialAuthService->getActiveDrivers();
         $this->setPageTitle(trans('settings.user_profile'));
         $roles = $this->userRepo->getAllRoles();
-        return view('users/edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
+        return view('users.edit', ['user' => $user, 'activeSocialDrivers' => $activeSocialDrivers, 'authMethod' => $authMethod, 'roles' => $roles]);
     }
 
     /**
@@ -190,7 +190,7 @@ class UserController extends Controller
 
         $user = $this->userRepo->getById($id);
         $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name]));
-        return view('users/delete', ['user' => $user]);
+        return view('users.delete', ['user' => $user]);
     }
 
     /**
@@ -232,10 +232,12 @@ class UserController extends Controller
     public function showProfilePage($id)
     {
         $user = $this->userRepo->getById($id);
+
         $userActivity = $this->userRepo->getActivity($user);
         $recentlyCreated = $this->userRepo->getRecentlyCreated($user, 5, 0);
         $assetCounts = $this->userRepo->getAssetCounts($user);
-        return view('users/profile', [
+
+        return view('users.profile', [
             'user' => $user,
             'activity' => $userActivity,
             'recentlyCreated' => $recentlyCreated,
@@ -251,41 +253,109 @@ class UserController extends Controller
      */
     public function switchBookView($id, Request $request)
     {
-        $this->checkPermissionOr('users-manage', function () use ($id) {
-            return $this->currentUser->id == $id;
-        });
+        return $this->switchViewType($id, $request, 'books');
+    }
+
+    /**
+     * Update the user's preferred shelf-list display setting.
+     * @param $id
+     * @param Request $request
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    public function switchShelfView($id, Request $request)
+    {
+        return $this->switchViewType($id, $request, 'bookshelves');
+    }
+
+    /**
+     * For a type of list, switch with stored view type for a user.
+     * @param integer $userId
+     * @param Request $request
+     * @param string $listName
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    protected function switchViewType($userId, Request $request, string $listName)
+    {
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
 
         $viewType = $request->get('view_type');
         if (!in_array($viewType, ['grid', 'list'])) {
             $viewType = 'list';
         }
 
-        $user = $this->user->findOrFail($id);
-        setting()->putUser($user, 'books_view_type', $viewType);
+        $user = $this->userRepo->getById($userId);
+        $key = $listName . '_view_type';
+        setting()->putUser($user, $key, $viewType);
 
-        return redirect()->back(302, [], "/settings/users/$id");
+        return redirect()->back(302, [], "/settings/users/$userId");
     }
 
     /**
-     * Update the user's preferred shelf-list display setting.
-     * @param $id
+     * Change the stored sort type for a particular view.
+     * @param string $id
+     * @param string $type
      * @param Request $request
      * @return \Illuminate\Http\RedirectResponse
      */
-    public function switchShelfView($id, Request $request)
+    public function changeSort(string $id, string $type, Request $request)
     {
-        $this->checkPermissionOr('users-manage', function () use ($id) {
-            return $this->currentUser->id == $id;
-        });
+        $validSortTypes = ['books', 'bookshelves'];
+        if (!in_array($type, $validSortTypes)) {
+            return redirect()->back(500);
+        }
+        return $this->changeListSort($id, $request, $type);
+    }
 
-        $viewType = $request->get('view_type');
-        if (!in_array($viewType, ['grid', 'list'])) {
-            $viewType = 'list';
+    /**
+     * Update the stored section expansion preference for the given user.
+     * @param string $id
+     * @param string $key
+     * @param Request $request
+     * @return \Illuminate\Contracts\Routing\ResponseFactory|\Symfony\Component\HttpFoundation\Response
+     */
+    public function updateExpansionPreference(string $id, string $key, Request $request)
+    {
+        $this->checkPermissionOrCurrentUser('users-manage', $id);
+        $keyWhitelist = ['home-details'];
+        if (!in_array($key, $keyWhitelist)) {
+            return response("Invalid key", 500);
         }
 
-        $user = $this->userRepo->getById($id);
-        setting()->putUser($user, 'bookshelves_view_type', $viewType);
+        $newState = $request->get('expand', 'false');
 
-        return redirect()->back(302, [], "/settings/users/$id");
+        $user = $this->user->findOrFail($id);
+        setting()->putUser($user, 'section_expansion#' . $key, $newState);
+        return response("", 204);
     }
+
+    /**
+     * Changed the stored preference for a list sort order.
+     * @param int $userId
+     * @param Request $request
+     * @param string $listName
+     * @return \Illuminate\Http\RedirectResponse
+     */
+    protected function changeListSort(int $userId, Request $request, string $listName)
+    {
+        $this->checkPermissionOrCurrentUser('users-manage', $userId);
+
+        $sort = $request->get('sort');
+        if (!in_array($sort, ['name', 'created_at', 'updated_at'])) {
+            $sort = 'name';
+        }
+
+        $order = $request->get('order');
+        if (!in_array($order, ['asc', 'desc'])) {
+            $order = 'asc';
+        }
+
+        $user = $this->user->findOrFail($userId);
+        $sortKey = $listName . '_sort';
+        $orderKey = $listName . '_sort_order';
+        setting()->putUser($user, $sortKey, $sort);
+        setting()->putUser($user, $orderKey, $order);
+
+        return redirect()->back(302, [], "/settings/users/$userId");
+    }
+
 }
index c9b2726e8c4eca4f9148f7567a035433bf216879..1a33843675a97266095cd6c2cb96918369652c9d 100644 (file)
@@ -37,7 +37,7 @@ class Authenticate
             }
         }
 
-        if ($this->auth->guest() && !setting('app-public')) {
+        if (!hasAppAccess()) {
             if ($request->ajax()) {
                 return response('Unauthorized.', 401);
             } else {
index 1d9a8736e6f66a1c5e4305a6a5869af08468f86a..9b91ba126c5878801a82f71f201a9dbbb753706a 100644 (file)
@@ -3,12 +3,14 @@
 use Blade;
 use BookStack\Entities\Book;
 use BookStack\Entities\Bookshelf;
+use BookStack\Entities\BreadcrumbsViewComposer;
 use BookStack\Entities\Chapter;
 use BookStack\Entities\Page;
 use BookStack\Settings\Setting;
 use BookStack\Settings\SettingService;
 use Illuminate\Database\Eloquent\Relations\Relation;
 use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\View;
 use Illuminate\Support\ServiceProvider;
 use Schema;
 use Validator;
@@ -28,6 +30,10 @@ class AppServiceProvider extends ServiceProvider
             return in_array(strtolower($value->getClientOriginalExtension()), $validImageExtensions);
         });
 
+        Validator::extend('no_double_extension', function ($attribute, $value, $parameters, $validator) {
+            $uploadName = $value->getClientOriginalName();
+            return substr_count($uploadName, '.') < 2;
+        });
 
         // Custom blade view directives
         Blade::directive('icon', function ($expression) {
@@ -44,6 +50,9 @@ class AppServiceProvider extends ServiceProvider
             'BookStack\\Chapter' => Chapter::class,
             'BookStack\\Page' => Page::class,
         ]);
+
+        // View Composers
+        View::composer('partials.breadcrumbs', BreadcrumbsViewComposer::class);
     }
 
     /**
index 5508ee9cd3e47920a10d68e96ee496592b8217db..e7bde5290490fc5d1fc06bf01c0c91c6de2afb55 100644 (file)
@@ -2,20 +2,11 @@
 
 namespace BookStack\Providers;
 
-use BookStack\Actions\Activity;
 use BookStack\Actions\ActivityService;
-use BookStack\Actions\View;
 use BookStack\Actions\ViewService;
-use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Settings\Setting;
 use BookStack\Settings\SettingService;
-use BookStack\Uploads\HttpFetcher;
-use BookStack\Uploads\Image;
 use BookStack\Uploads\ImageService;
-use Illuminate\Contracts\Cache\Repository;
-use Illuminate\Contracts\Filesystem\Factory;
 use Illuminate\Support\ServiceProvider;
-use Intervention\Image\ImageManager;
 
 class CustomFacadeProvider extends ServiceProvider
 {
@@ -37,34 +28,19 @@ class CustomFacadeProvider extends ServiceProvider
     public function register()
     {
         $this->app->bind('activity', function () {
-            return new ActivityService(
-                $this->app->make(Activity::class),
-                $this->app->make(PermissionService::class)
-            );
+            return $this->app->make(ActivityService::class);
         });
 
         $this->app->bind('views', function () {
-            return new ViewService(
-                $this->app->make(View::class),
-                $this->app->make(PermissionService::class)
-            );
+            return $this->app->make(ViewService::class);
         });
 
         $this->app->bind('setting', function () {
-            return new SettingService(
-                $this->app->make(Setting::class),
-                $this->app->make(Repository::class)
-            );
+            return $this->app->make(SettingService::class);
         });
 
         $this->app->bind('images', function () {
-            return new ImageService(
-                $this->app->make(Image::class),
-                $this->app->make(ImageManager::class),
-                $this->app->make(Factory::class),
-                $this->app->make(Repository::class),
-                $this->app->make(HttpFetcher::class)
-            );
+            return $this->app->make(ImageService::class);
         });
     }
 }
index c9491e3eeb4b4c949248680b9d43b1a0e45c3f59..489963aad7cde7b361ffdecfcb4e7d7edb4e3301 100644 (file)
@@ -61,9 +61,23 @@ class SettingService
      */
     public function getUser($user, $key, $default = false)
     {
+        if ($user->isDefault()) {
+            return session()->get($key, $default);
+        }
         return $this->get($this->userKey($user->id, $key), $default);
     }
 
+    /**
+     * Get a value for the current logged-in user.
+     * @param $key
+     * @param bool $default
+     * @return bool|string
+     */
+    public function getForCurrentUser($key, $default = false)
+    {
+        return $this->getUser(user(), $key, $default);
+    }
+
     /**
      * Gets a setting value from the cache or database.
      * Looks at the system defaults if not cached or in database.
@@ -180,6 +194,9 @@ class SettingService
      */
     public function putUser($user, $key, $value)
     {
+        if ($user->isDefault()) {
+            return session()->put($key, $value);
+        }
         return $this->put($this->userKey($user->id, $key), $value);
     }
 
index 7bafb6c0a26c050daab630a7f9ff692044708725..e613642c4f3dd8699a7c3f0c37b2c5394e468700 100644 (file)
@@ -44,7 +44,7 @@ class AttachmentService extends UploadService
     public function saveNewUpload(UploadedFile $uploadedFile, $page_id)
     {
         $attachmentName = $uploadedFile->getClientOriginalName();
-        $attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
+        $attachmentPath = $this->putFileInStorage($uploadedFile);
         $largestExistingOrder = Attachment::where('uploaded_to', '=', $page_id)->max('order');
 
         $attachment = Attachment::forceCreate([
@@ -75,7 +75,7 @@ class AttachmentService extends UploadService
         }
 
         $attachmentName = $uploadedFile->getClientOriginalName();
-        $attachmentPath = $this->putFileInStorage($attachmentName, $uploadedFile);
+        $attachmentPath = $this->putFileInStorage($uploadedFile);
 
         $attachment->name = $attachmentName;
         $attachment->path = $attachmentPath;
@@ -174,19 +174,18 @@ class AttachmentService extends UploadService
 
     /**
      * Store a file in storage with the given filename
-     * @param $attachmentName
      * @param UploadedFile $uploadedFile
      * @return string
      * @throws FileUploadException
      */
-    protected function putFileInStorage($attachmentName, UploadedFile $uploadedFile)
+    protected function putFileInStorage(UploadedFile $uploadedFile)
     {
         $attachmentData = file_get_contents($uploadedFile->getRealPath());
 
         $storage = $this->getStorage();
         $basePath = 'uploads/files/' . Date('Y-m-M') . '/';
 
-        $uploadFileName = $attachmentName;
+        $uploadFileName = str_random(16) . '.' . $uploadedFile->getClientOriginalExtension();
         while ($storage->exists($basePath . $uploadFileName)) {
             $uploadFileName = str_random(3) . $uploadFileName;
         }
index d9533645de6e31ae1c464b7466f1ee71a498a721..3f7b5e1b1350c1294adfee1b001775fb3a72b62b 100644 (file)
@@ -43,11 +43,19 @@ function user()
  * Check if current user is a signed in user.
  * @return bool
  */
-function signedInUser()
+function signedInUser() : bool
 {
     return auth()->user() && !auth()->user()->isDefault();
 }
 
+/**
+ * Check if the current user has general access.
+ * @return bool
+ */
+function hasAppAccess() : bool {
+    return !auth()->guest() || setting('app-public');
+}
+
 /**
  * Check if the current user has a permission.
  * If an ownable element is passed in the jointPermissions are checked against
index 67f90b863e0a51bfb63c05270c281da8a1bcac22..682462462a75c1ee6510eb18cc374c68cc65185f 100755 (executable)
@@ -52,7 +52,7 @@ return [
     'locale' => env('APP_LANG', 'en'),
 
     // Locales available
-    'locales' => ['en', 'ar', 'de', 'de_informal', 'es', 'es_AR', 'fr', 'nl', 'pt_BR', 'sk', 'sv', 'kr', 'ja', 'pl', 'it', 'ru', 'uk', 'zh_CN', 'zh_TW'],
+    'locales' => ['en', 'ar', 'de', 'de_informal', 'es', 'es_AR', 'fr', 'nl', 'pt_BR', 'sk', 'cs', 'sv', 'kr', 'ja', 'pl', 'it', 'ru', 'uk', 'zh_CN', 'zh_TW'],
 
     //  Application Fallback Locale
     'fallback_locale' => 'en',
index e1d1ace438c9049375aeff29c16c12320fce3ef8..1a5fef175c91884031624809093d9ac089a7722a 100644 (file)
       "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=",
       "dev": true
     },
-    "alphanum-sort": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz",
-      "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=",
-      "dev": true
-    },
     "amdefine": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
       "dev": true
     },
     "autoprefixer": {
-      "version": "8.6.5",
-      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-8.6.5.tgz",
-      "integrity": "sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==",
+      "version": "9.4.7",
+      "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.4.7.tgz",
+      "integrity": "sha512-qS5wW6aXHkm53Y4z73tFGsUhmZu4aMPV9iHXYlF0c/wxjknXNHuj/1cIQb+6YH692DbJGGWcckAXX+VxKvahMA==",
       "dev": true,
       "requires": {
-        "browserslist": "^3.2.8",
-        "caniuse-lite": "^1.0.30000864",
+        "browserslist": "^4.4.1",
+        "caniuse-lite": "^1.0.30000932",
         "normalize-range": "^0.1.2",
         "num2fraction": "^1.2.2",
-        "postcss": "^6.0.23",
-        "postcss-value-parser": "^3.2.3"
+        "postcss": "^7.0.14",
+        "postcss-value-parser": "^3.3.1"
       },
       "dependencies": {
         "browserslist": {
-          "version": "3.2.8",
-          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz",
-          "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==",
+          "version": "4.4.1",
+          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.1.tgz",
+          "integrity": "sha512-pEBxEXg7JwaakBXjATYw/D1YZh4QUSCX/Mnd/wnqSRPPSi1U39iDhDoKGoBUcraKdxDlrYqJxSI5nNvD+dWP2A==",
+          "dev": true,
+          "requires": {
+            "caniuse-lite": "^1.0.30000929",
+            "electron-to-chromium": "^1.3.103",
+            "node-releases": "^1.1.3"
+          }
+        },
+        "caniuse-lite": {
+          "version": "1.0.30000934",
+          "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000934.tgz",
+          "integrity": "sha512-o7yfZn0R9N+mWAuksDsdLsb1gu9o//XK0QSU0zSSReKNRsXsFc/n/psxi0YSPNiqlKxImp5h4DHnAPdwYJ8nNA==",
+          "dev": true
+        },
+        "electron-to-chromium": {
+          "version": "1.3.112",
+          "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.112.tgz",
+          "integrity": "sha512-FyVLdiRZnLw2WE5ECtveN0JJ7klyiz/HMfKE1/Rjff3l7pe4vfkYtBlcCqTckvR8E7asjJGh0m9gRPR3Anp/UA==",
+          "dev": true
+        },
+        "node-releases": {
+          "version": "1.1.7",
+          "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.7.tgz",
+          "integrity": "sha512-bKdrwaqJUPHqlCzDD7so/R+Nk0jGv9a11ZhLrD9f6i947qGLrGAhU3OxRENa19QQmwzGy/g6zCDEuLGDO8HPvA==",
           "dev": true,
           "requires": {
-            "caniuse-lite": "^1.0.30000844",
-            "electron-to-chromium": "^1.3.47"
+            "semver": "^5.3.0"
           }
         }
       }
         }
       }
     },
-    "babel-code-frame": {
-      "version": "6.26.0",
-      "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
-      "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3",
-        "esutils": "^2.0.2",
-        "js-tokens": "^3.0.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "js-tokens": {
-          "version": "3.0.2",
-          "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
-          "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
     "babel-loader": {
       "version": "8.0.4",
       "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.0.4.tgz",
       "dev": true
     },
     "cacache": {
-      "version": "10.0.4",
-      "resolved": "https://registry.npmjs.org/cacache/-/cacache-10.0.4.tgz",
-      "integrity": "sha512-Dph0MzuH+rTQzGPNT9fAnrPmMmjKfST6trxJeK7NQuHRaVw24VzPRWTmg9MpcwOVQZO0E1FBICUlFeNaKPIfHA==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.1",
-        "chownr": "^1.0.1",
-        "glob": "^7.1.2",
-        "graceful-fs": "^4.1.11",
-        "lru-cache": "^4.1.1",
-        "mississippi": "^2.0.0",
+      "version": "11.3.2",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
+      "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
+      "dev": true,
+      "requires": {
+        "bluebird": "^3.5.3",
+        "chownr": "^1.1.1",
+        "figgy-pudding": "^3.5.1",
+        "glob": "^7.1.3",
+        "graceful-fs": "^4.1.15",
+        "lru-cache": "^5.1.1",
+        "mississippi": "^3.0.0",
         "mkdirp": "^0.5.1",
         "move-concurrently": "^1.0.1",
         "promise-inflight": "^1.0.1",
         "rimraf": "^2.6.2",
-        "ssri": "^5.2.4",
-        "unique-filename": "^1.1.0",
+        "ssri": "^6.0.1",
+        "unique-filename": "^1.1.1",
         "y18n": "^4.0.0"
       },
       "dependencies": {
+        "bluebird": {
+          "version": "3.5.3",
+          "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.3.tgz",
+          "integrity": "sha512-/qKPUQlaW1OyR51WeCPBvRnAlnZFUJkCSG5HzGnuIqhgyJtF+T94lFnn33eiazjRm2LAHVy2guNnaq48X9SJuw==",
+          "dev": true
+        },
+        "graceful-fs": {
+          "version": "4.1.15",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
+          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+          "dev": true
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+          "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+          "dev": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
         "y18n": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
           "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
           "dev": true
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
+          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+          "dev": true
         }
       }
     },
         "map-obj": "^1.0.0"
       }
     },
-    "caniuse-api": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz",
-      "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=",
-      "dev": true,
-      "requires": {
-        "browserslist": "^1.3.6",
-        "caniuse-db": "^1.0.30000529",
-        "lodash.memoize": "^4.1.2",
-        "lodash.uniq": "^4.5.0"
-      },
-      "dependencies": {
-        "browserslist": {
-          "version": "1.7.7",
-          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
-          "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
-          "dev": true,
-          "requires": {
-            "caniuse-db": "^1.0.30000639",
-            "electron-to-chromium": "^1.2.7"
-          }
-        }
-      }
-    },
-    "caniuse-db": {
-      "version": "1.0.30000904",
-      "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000904.tgz",
-      "integrity": "sha512-iZ36AxtEx7ZiCBKhF2qFL8ED6u9zJGPU7Aq6HwZQYUbetBgYkGZfoPHq9z38jahV2kr8BgDYfXvftA35Ng2AaA==",
-      "dev": true
-    },
     "caniuse-lite": {
       "version": "1.0.30000904",
       "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30000904.tgz",
         "safe-buffer": "^5.0.1"
       }
     },
-    "clap": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz",
-      "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^1.1.3"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          }
-        },
-        "supports-color": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-          "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-          "dev": true
-        }
-      }
-    },
     "class-utils": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
         "wrap-ansi": "^2.0.0"
       }
     },
-    "clone": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
-      "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=",
-      "dev": true
-    },
     "clone-deep": {
       "version": "2.0.2",
       "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz",
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
-    "coa": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz",
-      "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=",
-      "dev": true,
-      "requires": {
-        "q": "^1.1.2"
-      }
-    },
     "code-point-at": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
         "object-visit": "^1.0.0"
       }
     },
-    "color": {
-      "version": "0.11.4",
-      "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz",
-      "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=",
-      "dev": true,
-      "requires": {
-        "clone": "^1.0.2",
-        "color-convert": "^1.3.0",
-        "color-string": "^0.3.0"
-      }
-    },
     "color-convert": {
       "version": "1.9.3",
       "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
-    "color-string": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz",
-      "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=",
-      "dev": true,
-      "requires": {
-        "color-name": "^1.0.0"
-      }
-    },
-    "colormin": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz",
-      "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=",
-      "dev": true,
-      "requires": {
-        "color": "^0.11.0",
-        "css-color-names": "0.0.4",
-        "has": "^1.0.1"
-      }
-    },
-    "colors": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz",
-      "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=",
-      "dev": true
-    },
     "combined-stream": {
       "version": "1.0.7",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz",
       }
     },
     "commander": {
-      "version": "2.13.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.13.0.tgz",
-      "integrity": "sha512-MVuS359B+YzaWqjCL/c+22gfryv+mCBPHAv3zyVI2GN8EY6IRP8VwtasXn8jyyhvvq84R4ImN1OKRtcbIasjYA==",
+      "version": "2.17.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.17.1.tgz",
+      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg==",
       "dev": true
     },
     "commondir": {
         "require-from-string": "^2.0.1"
       },
       "dependencies": {
-        "esprima": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
-          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-          "dev": true
-        },
-        "js-yaml": {
-          "version": "3.12.0",
-          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz",
-          "integrity": "sha512-PIt2cnwmPfL4hKNwqeiuz4bKfnzHTBv6HyVgjahA6mPLwPDzjDWrplJBMjHUFxku/N3FlmrbyPclad+I+4mJ3A==",
-          "dev": true,
-          "requires": {
-            "argparse": "^1.0.7",
-            "esprima": "^4.0.0"
-          }
-        },
         "parse-json": {
           "version": "4.0.0",
           "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
         "randomfill": "^1.0.3"
       }
     },
-    "css-color-names": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
-      "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=",
-      "dev": true
-    },
     "css-loader": {
-      "version": "0.28.11",
-      "resolved": "http://registry.npmjs.org/css-loader/-/css-loader-0.28.11.tgz",
-      "integrity": "sha512-wovHgjAx8ZIMGSL8pTys7edA1ClmzxHeY6n/d97gg5odgsxEgKjULPR0viqyC+FWMCL9sfqoC/QCUBo62tLvPg==",
-      "dev": true,
-      "requires": {
-        "babel-code-frame": "^6.26.0",
-        "css-selector-tokenizer": "^0.7.0",
-        "cssnano": "^3.10.0",
-        "icss-utils": "^2.1.0",
-        "loader-utils": "^1.0.2",
-        "lodash.camelcase": "^4.3.0",
-        "object-assign": "^4.1.1",
-        "postcss": "^5.0.6",
-        "postcss-modules-extract-imports": "^1.2.0",
-        "postcss-modules-local-by-default": "^1.2.0",
-        "postcss-modules-scope": "^1.1.0",
-        "postcss-modules-values": "^1.3.0",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.0.tgz",
+      "integrity": "sha512-MoOu+CStsGrSt5K2OeZ89q3Snf+IkxRfAIt9aAKg4piioTrhtP1iEFPu+OVn3Ohz24FO6L+rw9UJxBILiSBw5Q==",
+      "dev": true,
+      "requires": {
+        "icss-utils": "^4.0.0",
+        "loader-utils": "^1.2.1",
+        "lodash": "^4.17.11",
+        "postcss": "^7.0.6",
+        "postcss-modules-extract-imports": "^2.0.0",
+        "postcss-modules-local-by-default": "^2.0.3",
+        "postcss-modules-scope": "^2.0.0",
+        "postcss-modules-values": "^2.0.0",
         "postcss-value-parser": "^3.3.0",
-        "source-list-map": "^2.0.0"
+        "schema-utils": "^1.0.0"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+        "ajv": {
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
           }
         },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
+        "big.js": {
+          "version": "5.2.2",
+          "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
           "dev": true
         },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "json5": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
           "dev": true,
           "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
+            "minimist": "^1.2.0"
           }
         },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+        "loader-utils": {
+          "version": "1.2.3",
+          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+          "dev": true,
+          "requires": {
+            "big.js": "^5.2.2",
+            "emojis-list": "^2.0.0",
+            "json5": "^1.0.1"
+          }
+        },
+        "lodash": {
+          "version": "4.17.11",
+          "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz",
+          "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==",
           "dev": true
         },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+        "minimist": {
+          "version": "1.2.0",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
           "dev": true,
           "requires": {
-            "has-flag": "^1.0.0"
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
           }
         }
       }
         },
         "regjsgen": {
           "version": "0.2.0",
-          "resolved": "http://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
+          "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",
           "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=",
           "dev": true
         },
       "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=",
       "dev": true
     },
-    "cssnano": {
-      "version": "3.10.0",
-      "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz",
-      "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=",
+    "currently-unhandled": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
       "dev": true,
       "requires": {
-        "autoprefixer": "^6.3.1",
-        "decamelize": "^1.1.2",
-        "defined": "^1.0.0",
-        "has": "^1.0.1",
-        "object-assign": "^4.0.1",
-        "postcss": "^5.0.14",
-        "postcss-calc": "^5.2.0",
-        "postcss-colormin": "^2.1.8",
-        "postcss-convert-values": "^2.3.4",
-        "postcss-discard-comments": "^2.0.4",
-        "postcss-discard-duplicates": "^2.0.1",
-        "postcss-discard-empty": "^2.0.1",
-        "postcss-discard-overridden": "^0.1.1",
-        "postcss-discard-unused": "^2.2.1",
-        "postcss-filter-plugins": "^2.0.0",
-        "postcss-merge-idents": "^2.1.5",
-        "postcss-merge-longhand": "^2.0.1",
-        "postcss-merge-rules": "^2.0.3",
-        "postcss-minify-font-values": "^1.0.2",
-        "postcss-minify-gradients": "^1.0.1",
-        "postcss-minify-params": "^1.0.4",
-        "postcss-minify-selectors": "^2.0.4",
-        "postcss-normalize-charset": "^1.1.0",
-        "postcss-normalize-url": "^3.0.7",
-        "postcss-ordered-values": "^2.1.0",
-        "postcss-reduce-idents": "^2.2.2",
-        "postcss-reduce-initial": "^1.0.0",
-        "postcss-reduce-transforms": "^1.0.3",
-        "postcss-svgo": "^2.1.1",
-        "postcss-unique-selectors": "^2.0.2",
-        "postcss-value-parser": "^3.2.3",
-        "postcss-zindex": "^2.0.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "autoprefixer": {
-          "version": "6.7.7",
-          "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz",
-          "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=",
-          "dev": true,
-          "requires": {
-            "browserslist": "^1.7.6",
-            "caniuse-db": "^1.0.30000634",
-            "normalize-range": "^0.1.2",
-            "num2fraction": "^1.2.2",
-            "postcss": "^5.2.16",
-            "postcss-value-parser": "^3.2.3"
-          }
-        },
-        "browserslist": {
-          "version": "1.7.7",
-          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
-          "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
-          "dev": true,
-          "requires": {
-            "caniuse-db": "^1.0.30000639",
-            "electron-to-chromium": "^1.2.7"
-          }
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "csso": {
-      "version": "2.3.2",
-      "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz",
-      "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=",
-      "dev": true,
-      "requires": {
-        "clap": "^1.0.9",
-        "source-map": "^0.5.3"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        }
-      }
-    },
-    "currently-unhandled": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
-      "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
-      "dev": true,
-      "requires": {
-        "array-find-index": "^1.0.1"
-      }
-    },
-    "cyclist": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
-      "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
-      "dev": true
-    },
-    "dashdash": {
-      "version": "1.14.1",
-      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
-      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "date-now": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
-      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
-      "dev": true
-    },
-    "debug": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
-      "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
-      "dev": true,
-      "requires": {
-        "ms": "^2.1.1"
+        "array-find-index": "^1.0.1"
+      }
+    },
+    "cyclist": {
+      "version": "0.2.2",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
+      "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
+      "dev": true
+    },
+    "dashdash": {
+      "version": "1.14.1",
+      "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+      "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+      "dev": true,
+      "requires": {
+        "assert-plus": "^1.0.0"
+      }
+    },
+    "date-now": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
+      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
+      "dev": true
+    },
+    "debug": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.0.tgz",
+      "integrity": "sha512-heNPJUJIqC+xB6ayLAMHaIrmN9HKa7aQO8MGqKpvCA+uJYVcvR6l5kgdrhRuwPFHU7P5/A1w0BjByPHwpfTDKg==",
+      "dev": true,
+      "requires": {
+        "ms": "^2.1.1"
       },
       "dependencies": {
         "ms": {
         }
       }
     },
-    "defined": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
-      "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=",
-      "dev": true
-    },
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       }
     },
     "esprima": {
-      "version": "2.7.3",
-      "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
-      "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+      "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
       "dev": true
     },
     "esrecurse": {
         "is-extglob": "^1.0.0"
       }
     },
-    "extract-text-webpack-plugin": {
-      "version": "4.0.0-beta.0",
-      "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-4.0.0-beta.0.tgz",
-      "integrity": "sha512-Hypkn9jUTnFr0DpekNam53X47tXn3ucY08BQumv7kdGgeVUBLq3DJHJTi6HNxv4jl9W+Skxjz9+RnK0sJyqqjA==",
-      "dev": true,
-      "requires": {
-        "async": "^2.4.1",
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^0.4.5",
-        "webpack-sources": "^1.1.0"
-      },
-      "dependencies": {
-        "async": {
-          "version": "2.6.0",
-          "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz",
-          "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==",
-          "dev": true,
-          "requires": {
-            "lodash": "^4.14.0"
-          }
-        }
-      }
-    },
     "extsprintf": {
       "version": "1.3.0",
       "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
         "locate-path": "^2.0.0"
       }
     },
-    "flatten": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz",
-      "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=",
-      "dev": true
-    },
     "flush-write-stream": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
         "code-point-at": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "concat-map": {
           "version": "0.0.1",
         "console-control-strings": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "core-util-is": {
           "version": "1.0.2",
         "inherits": {
           "version": "2.0.3",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "ini": {
           "version": "1.3.5",
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true
+          "dev": true,
+          "optional": true
         },
         "object-assign": {
           "version": "4.1.1",
           "version": "1.4.0",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "wrappy": "1"
           }
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
+          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
       "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
       "dev": true
     },
-    "html-comment-regex": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.2.tgz",
-      "integrity": "sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==",
-      "dev": true
-    },
     "http-signature": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
       "dev": true
     },
     "icss-utils": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-2.1.0.tgz",
-      "integrity": "sha1-g/Cg7DeL8yRheLbCrZE28TWxyWI=",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.0.0.tgz",
+      "integrity": "sha512-bA/xGiwWM17qjllIs9X/y0EjsB7e0AV08F3OL8UPsoNkNRibIuu8f1eKTnQ8QO1DteKKTxTUAn+IEWUToIwGOA==",
       "dev": true,
       "requires": {
-        "postcss": "^6.0.1"
+        "postcss": "^7.0.5"
       }
     },
     "ieee754": {
         "repeating": "^2.0.0"
       }
     },
-    "indexes-of": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz",
-      "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
-      "dev": true
-    },
     "indexof": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
       "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
       "dev": true
     },
-    "is-absolute-url": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz",
-      "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=",
-      "dev": true
-    },
     "is-accessor-descriptor": {
       "version": "0.1.6",
       "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
         "kind-of": "^3.0.2"
       }
     },
-    "is-plain-obj": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
-      "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=",
-      "dev": true
-    },
     "is-plain-object": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
       "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
       "dev": true
     },
-    "is-svg": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz",
-      "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=",
-      "dev": true,
-      "requires": {
-        "html-comment-regex": "^1.1.0"
-      }
-    },
     "is-symbol": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
       "dev": true
     },
     "js-yaml": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz",
-      "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=",
+      "version": "3.12.1",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+      "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
       "dev": true,
       "requires": {
         "argparse": "^1.0.7",
-        "esprima": "^2.6.0"
+        "esprima": "^4.0.0"
       }
     },
     "jsbn": {
       "integrity": "sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=",
       "dev": true
     },
-    "lodash.camelcase": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
-      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=",
-      "dev": true
-    },
     "lodash.clonedeep": {
       "version": "4.5.0",
       "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
       "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
       "dev": true
     },
-    "lodash.memoize": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
-      "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=",
-      "dev": true
-    },
     "lodash.mergewith": {
       "version": "4.6.1",
       "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz",
       "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
       "dev": true
     },
-    "lodash.uniq": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
-      "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
-      "dev": true
-    },
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
       "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
       "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
     },
-    "math-expression-evaluator": {
-      "version": "1.2.17",
-      "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz",
-      "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=",
-      "dev": true
-    },
     "math-random": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.1.tgz",
       "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==",
       "dev": true
     },
+    "mini-css-extract-plugin": {
+      "version": "0.5.0",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.5.0.tgz",
+      "integrity": "sha512-IuaLjruM0vMKhUUT51fQdQzBYTX49dLj8w68ALEAe2A4iYNpIC4eMac67mt3NzycvjOlf07/kYxJDc0RTl1Wqw==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "schema-utils": "^1.0.0",
+        "webpack-sources": "^1.1.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        }
+      }
+    },
     "minimalistic-assert": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
       "dev": true
     },
     "mississippi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-2.0.0.tgz",
-      "integrity": "sha512-zHo8v+otD1J10j/tC+VNoGK9keCuByhKovAvdn74dmxJl9+mWHnx6EMsDN4lgRoMI/eYo2nchAxniIbUPb5onw==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz",
+      "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==",
       "dev": true,
       "requires": {
         "concat-stream": "^1.5.0",
         "flush-write-stream": "^1.0.0",
         "from2": "^2.1.0",
         "parallel-transform": "^1.1.0",
-        "pump": "^2.0.1",
+        "pump": "^3.0.0",
         "pumpify": "^1.3.3",
         "stream-each": "^1.1.0",
         "through2": "^2.0.0"
+      },
+      "dependencies": {
+        "pump": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
+          "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
+          "dev": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        }
       }
     },
     "mixin-deep": {
       "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=",
       "dev": true
     },
-    "normalize-url": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz",
-      "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.0.1",
-        "prepend-http": "^1.0.0",
-        "query-string": "^4.1.0",
-        "sort-keys": "^1.0.0"
-      }
-    },
     "npm-run-all": {
       "version": "4.1.5",
       "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
       "dev": true
     },
     "postcss": {
-      "version": "6.0.23",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.23.tgz",
-      "integrity": "sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==",
+      "version": "7.0.14",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.14.tgz",
+      "integrity": "sha512-NsbD6XUUMZvBxtQAJuWDJeeC4QFsmWsfozWxCJPWf3M55K9iu2iMDaKqyoOdTJ1R4usBXuxlVFAIo8rZPQD4Bg==",
       "dev": true,
       "requires": {
-        "chalk": "^2.4.1",
+        "chalk": "^2.4.2",
         "source-map": "^0.6.1",
-        "supports-color": "^5.4.0"
-      }
-    },
-    "postcss-calc": {
-      "version": "5.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz",
-      "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.2",
-        "postcss-message-helpers": "^2.0.0",
-        "reduce-css-calc": "^1.2.6"
+        "supports-color": "^6.1.0"
       },
       "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
         "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
           "dev": true,
           "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
           },
           "dependencies": {
             "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
+              "version": "5.5.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+              "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+              "dev": true,
+              "requires": {
+                "has-flag": "^3.0.0"
+              }
             }
           }
         },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
           "dev": true,
           "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-colormin": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz",
-      "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=",
-      "dev": true,
-      "requires": {
-        "colormin": "^1.0.5",
-        "postcss": "^5.0.13",
-        "postcss-value-parser": "^3.2.3"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-convert-values": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz",
-      "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.11",
-        "postcss-value-parser": "^3.1.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-discard-comments": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz",
-      "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.14"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-discard-duplicates": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz",
-      "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-discard-empty": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz",
-      "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.14"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-discard-overridden": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz",
-      "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.16"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-discard-unused": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz",
-      "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.14",
-        "uniqs": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-filter-plugins": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz",
-      "integrity": "sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-load-config": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz",
-      "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==",
-      "dev": true,
-      "requires": {
-        "cosmiconfig": "^4.0.0",
-        "import-cwd": "^2.0.0"
-      }
-    },
-    "postcss-loader": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-2.1.6.tgz",
-      "integrity": "sha512-hgiWSc13xVQAq25cVw80CH0l49ZKlAnU1hKPOdRrNj89bokRr/bZF2nT+hebPPF9c9xs8c3gw3Fr2nxtmXYnNg==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.1.0",
-        "postcss": "^6.0.0",
-        "postcss-load-config": "^2.0.0",
-        "schema-utils": "^0.4.0"
-      }
-    },
-    "postcss-merge-idents": {
-      "version": "2.1.7",
-      "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz",
-      "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1",
-        "postcss": "^5.0.10",
-        "postcss-value-parser": "^3.1.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-merge-longhand": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz",
-      "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-merge-rules": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz",
-      "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=",
-      "dev": true,
-      "requires": {
-        "browserslist": "^1.5.2",
-        "caniuse-api": "^1.5.2",
-        "postcss": "^5.0.4",
-        "postcss-selector-parser": "^2.2.2",
-        "vendors": "^1.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "browserslist": {
-          "version": "1.7.7",
-          "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz",
-          "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=",
-          "dev": true,
-          "requires": {
-            "caniuse-db": "^1.0.30000639",
-            "electron-to-chromium": "^1.2.7"
-          }
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-message-helpers": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz",
-      "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=",
-      "dev": true
-    },
-    "postcss-minify-font-values": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz",
-      "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.0.1",
-        "postcss": "^5.0.4",
-        "postcss-value-parser": "^3.0.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-minify-gradients": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz",
-      "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.12",
-        "postcss-value-parser": "^3.3.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-minify-params": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz",
-      "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=",
-      "dev": true,
-      "requires": {
-        "alphanum-sort": "^1.0.1",
-        "postcss": "^5.0.2",
-        "postcss-value-parser": "^3.0.2",
-        "uniqs": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-minify-selectors": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz",
-      "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=",
-      "dev": true,
-      "requires": {
-        "alphanum-sort": "^1.0.2",
-        "has": "^1.0.1",
-        "postcss": "^5.0.14",
-        "postcss-selector-parser": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-modules-extract-imports": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.1.tgz",
-      "integrity": "sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==",
-      "dev": true,
-      "requires": {
-        "postcss": "^6.0.1"
-      }
-    },
-    "postcss-modules-local-by-default": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz",
-      "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=",
-      "dev": true,
-      "requires": {
-        "css-selector-tokenizer": "^0.7.0",
-        "postcss": "^6.0.1"
-      }
-    },
-    "postcss-modules-scope": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz",
-      "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=",
-      "dev": true,
-      "requires": {
-        "css-selector-tokenizer": "^0.7.0",
-        "postcss": "^6.0.1"
-      }
-    },
-    "postcss-modules-values": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz",
-      "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=",
-      "dev": true,
-      "requires": {
-        "icss-replace-symbols": "^1.1.0",
-        "postcss": "^6.0.1"
-      }
-    },
-    "postcss-normalize-charset": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz",
-      "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.5"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-normalize-url": {
-      "version": "3.0.8",
-      "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz",
-      "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=",
-      "dev": true,
-      "requires": {
-        "is-absolute-url": "^2.0.0",
-        "normalize-url": "^1.4.0",
-        "postcss": "^5.0.14",
-        "postcss-value-parser": "^3.2.3"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-ordered-values": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz",
-      "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4",
-        "postcss-value-parser": "^3.0.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-reduce-idents": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz",
-      "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4",
-        "postcss-value-parser": "^3.0.2"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-reduce-initial": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz",
-      "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=",
-      "dev": true,
-      "requires": {
-        "postcss": "^5.0.4"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-reduce-transforms": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz",
-      "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1",
-        "postcss": "^5.0.8",
-        "postcss-value-parser": "^3.0.1"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "2.2.3",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz",
-      "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=",
-      "dev": true,
-      "requires": {
-        "flatten": "^1.0.2",
-        "indexes-of": "^1.0.1",
-        "uniq": "^1.0.1"
-      }
-    },
-    "postcss-svgo": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz",
-      "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=",
-      "dev": true,
-      "requires": {
-        "is-svg": "^2.0.0",
-        "postcss": "^5.0.14",
-        "postcss-value-parser": "^3.2.3",
-        "svgo": "^0.7.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
+            "has-flag": "^3.0.0"
           }
         }
       }
     },
-    "postcss-unique-selectors": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz",
-      "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=",
+    "postcss-load-config": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-2.0.0.tgz",
+      "integrity": "sha512-V5JBLzw406BB8UIfsAWSK2KSwIJ5yoEIVFb4gVkXci0QdKgA24jLmHZ/ghe/GgX0lJ0/D1uUK1ejhzEY94MChQ==",
       "dev": true,
       "requires": {
-        "alphanum-sort": "^1.0.1",
-        "postcss": "^5.0.4",
-        "uniqs": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
-          "dev": true,
-          "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
-          "dev": true,
-          "requires": {
-            "has-flag": "^1.0.0"
-          }
-        }
+        "cosmiconfig": "^4.0.0",
+        "import-cwd": "^2.0.0"
       }
     },
-    "postcss-value-parser": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
-      "dev": true
-    },
-    "postcss-zindex": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz",
-      "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1",
-        "postcss": "^5.0.4",
-        "uniqs": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
-          "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
-          "dev": true
-        },
-        "chalk": {
-          "version": "1.1.3",
-          "resolved": "http://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
-          "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^2.2.1",
-            "escape-string-regexp": "^1.0.2",
-            "has-ansi": "^2.0.0",
-            "strip-ansi": "^3.0.0",
-            "supports-color": "^2.0.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "version": "2.0.0",
-              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
-              "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
-              "dev": true
-            }
-          }
-        },
-        "has-flag": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz",
-          "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=",
-          "dev": true
-        },
-        "postcss": {
-          "version": "5.2.18",
-          "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz",
-          "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==",
+    "postcss-loader": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-3.0.0.tgz",
+      "integrity": "sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA==",
+      "dev": true,
+      "requires": {
+        "loader-utils": "^1.1.0",
+        "postcss": "^7.0.0",
+        "postcss-load-config": "^2.0.0",
+        "schema-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
           "dev": true,
           "requires": {
-            "chalk": "^1.1.3",
-            "js-base64": "^2.1.9",
-            "source-map": "^0.5.6",
-            "supports-color": "^3.2.3"
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
           }
         },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
           "dev": true
         },
-        "supports-color": {
-          "version": "3.2.3",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
-          "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=",
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
           "dev": true,
           "requires": {
-            "has-flag": "^1.0.0"
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
           }
         }
       }
     },
-    "prepend-http": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
+    "postcss-modules-extract-imports": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz",
+      "integrity": "sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ==",
+      "dev": true,
+      "requires": {
+        "postcss": "^7.0.5"
+      }
+    },
+    "postcss-modules-local-by-default": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.4.tgz",
+      "integrity": "sha512-WvuSaTKXUqYJbnT7R3YrsNrHv/C5vRfr5VglS4bFOk0MYT4CLBfc/xgExA+x2RftlYgiBDvWmVs191Xv8S8gZQ==",
+      "dev": true,
+      "requires": {
+        "css-selector-tokenizer": "^0.7.0",
+        "postcss": "^7.0.6",
+        "postcss-value-parser": "^3.3.1"
+      }
+    },
+    "postcss-modules-scope": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.0.1.tgz",
+      "integrity": "sha512-7+6k9c3/AuZ5c596LJx9n923A/j3nF3ormewYBF1RrIQvjvjXe1xE8V8A1KFyFwXbvnshT6FBZFX0k/F1igneg==",
+      "dev": true,
+      "requires": {
+        "css-selector-tokenizer": "^0.7.0",
+        "postcss": "^7.0.6"
+      }
+    },
+    "postcss-modules-values": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz",
+      "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==",
+      "dev": true,
+      "requires": {
+        "icss-replace-symbols": "^1.1.0",
+        "postcss": "^7.0.6"
+      }
+    },
+    "postcss-value-parser": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
+      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
       "dev": true
     },
     "preserve": {
       "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
       "dev": true
     },
-    "q": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
-      "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=",
-      "dev": true
-    },
     "qs": {
       "version": "6.5.2",
       "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
       "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
       "dev": true
     },
-    "query-string": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
-      "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.1.0",
-        "strict-uri-encode": "^1.0.0"
-      }
-    },
     "querystring": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
         "strip-indent": "^1.0.1"
       }
     },
-    "reduce-css-calc": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz",
-      "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^0.4.2",
-        "math-expression-evaluator": "^1.2.14",
-        "reduce-function-call": "^1.0.1"
-      },
-      "dependencies": {
-        "balanced-match": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
-          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
-          "dev": true
-        }
-      }
-    },
-    "reduce-function-call": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz",
-      "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=",
-      "dev": true,
-      "requires": {
-        "balanced-match": "^0.4.2"
-      },
-      "dependencies": {
-        "balanced-match": {
-          "version": "0.4.2",
-          "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz",
-          "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=",
-          "dev": true
-        }
-      }
-    },
     "regenerate": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
         "semver": "^5.5.0"
       }
     },
-    "sax": {
-      "version": "1.2.4",
-      "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
-      "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
-      "dev": true
-    },
     "schema-utils": {
       "version": "0.4.5",
       "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.5.tgz",
         "kind-of": "^3.2.0"
       }
     },
-    "sort-keys": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz",
-      "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=",
-      "dev": true,
-      "requires": {
-        "is-plain-obj": "^1.0.0"
-      }
-    },
     "sortablejs": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.7.0.tgz",
       }
     },
     "ssri": {
-      "version": "5.3.0",
-      "resolved": "https://registry.npmjs.org/ssri/-/ssri-5.3.0.tgz",
-      "integrity": "sha512-XRSIPqLij52MtgoQavH/x/dU1qVKtWUAAZeOHsR9c2Ddi4XerFy3mc1alf+dLJKl9EUIm/Ht+EowFkTUOA6GAQ==",
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz",
+      "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.1.1"
+        "figgy-pudding": "^3.5.1"
       }
     },
     "static-extend": {
       "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
       "dev": true
     },
-    "strict-uri-encode": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
-      "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=",
-      "dev": true
-    },
     "string-width": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
       }
     },
     "style-loader": {
-      "version": "0.21.0",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.21.0.tgz",
-      "integrity": "sha512-T+UNsAcl3Yg+BsPKs1vd22Fr8sVT+CJMtzqc6LEw9bbJZb43lm9GoeIfUcDEefBSWC0BhYbcdupV1GtI4DGzxg==",
+      "version": "0.23.1",
+      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
+      "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
       "dev": true,
       "requires": {
         "loader-utils": "^1.1.0",
-        "schema-utils": "^0.4.5"
+        "schema-utils": "^1.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        }
       }
     },
     "supports-color": {
         "has-flag": "^3.0.0"
       }
     },
-    "svgo": {
-      "version": "0.7.2",
-      "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz",
-      "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=",
-      "dev": true,
-      "requires": {
-        "coa": "~1.0.1",
-        "colors": "~1.1.2",
-        "csso": "~2.3.1",
-        "js-yaml": "~3.7.0",
-        "mkdirp": "~0.5.1",
-        "sax": "~1.2.1",
-        "whet.extend": "~0.9.9"
-      }
-    },
     "tapable": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.0.tgz",
       "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
       "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
     },
-    "uglify-es": {
-      "version": "3.3.9",
-      "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz",
-      "integrity": "sha512-r+MU0rfv4L/0eeW3xZrd16t4NZfK8Ld4SWVglYBb7ez5uXFWHuVRs6xCTrf1yirs9a4j4Y27nn7SRfO6v67XsQ==",
+    "uglify-js": {
+      "version": "3.4.9",
+      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.4.9.tgz",
+      "integrity": "sha512-8CJsbKOtEbnJsTyv6LE6m6ZKniqMiFWmm9sRbopbkGs3gMPPfd3Fh8iIA4Ykv5MgaTbqHr4BaoGLJLZNhsrW1Q==",
       "dev": true,
       "requires": {
-        "commander": "~2.13.0",
+        "commander": "~2.17.1",
         "source-map": "~0.6.1"
       }
     },
     "uglifyjs-webpack-plugin": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.3.0.tgz",
-      "integrity": "sha512-ovHIch0AMlxjD/97j9AYovZxG5wnHOPkL7T1GKochBADp/Zwc44pEWNqpKl1Loupp1WhFg7SlYmHZRUfdAacgw==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-2.1.1.tgz",
+      "integrity": "sha512-TQEcyMNkObX/H+FfcKjiDgs5RcXX8vW2UUUrDTOfQgg3lrafztfeM5WAwXo+AzqozJK6NP9w98xNpG/dutzSsg==",
       "dev": true,
       "requires": {
-        "cacache": "^10.0.4",
-        "find-cache-dir": "^1.0.0",
-        "schema-utils": "^0.4.5",
+        "cacache": "^11.2.0",
+        "find-cache-dir": "^2.0.0",
+        "schema-utils": "^1.0.0",
         "serialize-javascript": "^1.4.0",
         "source-map": "^0.6.1",
-        "uglify-es": "^3.3.4",
+        "uglify-js": "^3.0.0",
         "webpack-sources": "^1.1.0",
         "worker-farm": "^1.5.2"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.8.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.8.1.tgz",
+          "integrity": "sha512-eqxCp82P+JfqL683wwsL73XmFs1eG6qjw+RD3YHx+Jll1r0jNd4dh8QG9NYAeNGA/hnZjeEDgtTskgJULbxpWQ==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^2.0.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
+          "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
+          "dev": true
+        },
+        "find-cache-dir": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.0.0.tgz",
+          "integrity": "sha512-LDUY6V1Xs5eFskUVYtIwatojt6+9xC9Chnlk/jYOOvn3FAFfSaWddxahDGyNHh0b2dMXa6YW2m0tk8TdVaXHlA==",
+          "dev": true,
+          "requires": {
+            "commondir": "^1.0.1",
+            "make-dir": "^1.0.0",
+            "pkg-dir": "^3.0.0"
+          }
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "0.4.1",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+          "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+          "dev": true
+        },
+        "locate-path": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+          "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^3.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.1.0.tgz",
+          "integrity": "sha512-NhURkNcrVB+8hNfLuysU8enY5xn2KXphsHBaC2YmRNTZRc7RWusw6apSpdEj3jo4CMb6W9nrF6tTnsJsJeyu6g==",
+          "dev": true,
+          "requires": {
+            "p-try": "^2.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+          "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^2.0.0"
+          }
+        },
+        "p-try": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
+          "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==",
+          "dev": true
+        },
+        "pkg-dir": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz",
+          "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==",
+          "dev": true,
+          "requires": {
+            "find-up": "^3.0.0"
+          }
+        },
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        }
       }
     },
     "ultron": {
         }
       }
     },
-    "uniq": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
-      "dev": true
-    },
-    "uniqs": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz",
-      "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=",
-      "dev": true
-    },
     "unique-filename": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
         "spdx-expression-parse": "^3.0.0"
       }
     },
-    "vendors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.2.tgz",
-      "integrity": "sha512-w/hry/368nO21AN9QljsaIhb9ZiZtZARoVH5f3CsFbawdLdayCgKRPup7CggujvySMxx0I91NOyxdVENohprLQ==",
-      "dev": true
-    },
     "verror": {
       "version": "1.10.0",
       "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
         "source-map": "~0.6.1"
       }
     },
-    "whet.extend": {
-      "version": "0.9.9",
-      "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz",
-      "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=",
-      "dev": true
-    },
     "which": {
       "version": "1.3.1",
       "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
index dc90689d3d86b6406d94b9ee723cb09fd3d63e50..1cbfdb3e9ba9d62eef4801419ac0ebea20e94d45 100644 (file)
     "@babel/core": "^7.1.6",
     "@babel/polyfill": "^7.0.0",
     "@babel/preset-env": "^7.1.6",
-    "autoprefixer": "^8.6.5",
+    "autoprefixer": "^9.4.7",
     "babel-loader": "^8.0.4",
-    "css-loader": "^0.28.11",
-    "extract-text-webpack-plugin": "^4.0.0-beta.0",
+    "css-loader": "^2.1.0",
     "livereload": "^0.7.0",
+    "mini-css-extract-plugin": "^0.5.0",
     "node-sass": "^4.10.0",
     "npm-run-all": "^4.1.5",
-    "postcss-loader": "^2.1.6",
+    "postcss-loader": "^3.0.0",
     "sass-loader": "^7.1.0",
-    "style-loader": "^0.21.0",
-    "uglifyjs-webpack-plugin": "^1.3.0",
+    "style-loader": "^0.23.1",
+    "uglifyjs-webpack-plugin": "^2.1.1",
     "webpack": "^4.26.1",
     "webpack-cli": "^3.1.2"
   },
index 037fbedb5eced2af6de1350b3f0643e4ab6c193c..940deb04ca2b029b753207f544f86129eb835669 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -117,6 +117,15 @@ If you are looking to alter CSS or JavaScript content please edit the source fil
 
 The website which contains the project docs & Blog can be found in the [BookStackApp/website](https://github.com/BookStackApp/website) repo.
 
+## Security
+
+Security information for administering a BookStack instance can be found on the [documentation site here](https://www.bookstackapp.com/docs/admin/security/).
+
+If you'd like to be notified of new potential security concerns you can [sign-up to the BookStack security mailing list](http://eepurl.com/glIh8z).
+
+If you would like to report a security concern in a more confidential manner than via a GitHub issue, You can directly email the lead maintainer [ssddanbrown](https://github.com/ssddanbrown). You will need to login to be able to see the email address on the [GitHub profile page](https://github.com/ssddanbrown). Alternatively you can send a DM via twitter to [@ssddanbrown](https://twitter.com/ssddanbrown).
+
+
 ## License
 
 The BookStack source is provided under the MIT License.
index 75e3753dc4147668c1c5a1d21b14b036ee9dc948..edd367b2d8c17c8e5d65b7e2ae14e729ac949193 100644 (file)
@@ -1,4 +1 @@
-<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-    <path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
-    <path d="M0 0h24v24H0z" fill="none"/>
-</svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 13.3h-5.7V19h-2.6v-5.7H5v-2.6h5.7V5h2.6v5.7H19z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/books.svg b/resources/assets/icons/books.svg
new file mode 100644 (file)
index 0000000..a485a01
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M0 0h24v24H0z" fill="none"/><path d="M19.252 1.708H8.663a1.77 1.77 0 0 0-1.765 1.764v14.12c0 .97.794 1.764 1.765 1.764h10.59a1.77 1.77 0 0 0 1.764-1.765V3.472a1.77 1.77 0 0 0-1.765-1.764zM8.663 3.472h4.412v7.06L10.87 9.208l-2.206 1.324z"/><path d="M30.61 3.203h24v24h-24z" fill="none"/><path d="M2.966 6.61v14c0 1.1.9 2 2 2h14v-2h-14v-14z"/></svg>
\ No newline at end of file
index 03da68f9629a049c4e8adde61080eb2fc6e51e00..f1e45eaf99639e344fb6ede008eab2dafb8abf36 100644 (file)
@@ -1,2 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M1.088 2.566h17.42v17.42H1.088z" fill="none"/><path d="M4 20.058h15.892V22H4z"/><path d="M2.902 1.477h17.42v17.42H2.903z" fill="none"/><g><path d="M6.658 3.643V18h-2.38V3.643zM11.326 3.643V18H8.947V3.643zM14.722 3.856l5.613 13.214-2.19.93-5.613-13.214z"/></g></svg>
-
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M1.088 2.566h17.42v17.42H1.088z" fill="none"/><path d="M4 20.058h15.892V22H4z"/><path d="M2.902 1.477h17.42v17.42H2.903z" fill="none"/><g><path d="M6.658 3.643V18h-2.38V3.643zM11.326 3.643V18H8.947V3.643zM14.722 3.856l5.613 13.214-2.19.93-5.613-13.214z"/></g></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/check.svg b/resources/assets/icons/check.svg
new file mode 100644 (file)
index 0000000..93607ef
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18.86 4.118l-9.733 9.609-3.951-3.995-2.98 2.966 6.93 7.184L21.805 7.217z"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/chevron-right.svg b/resources/assets/icons/chevron-right.svg
new file mode 100644 (file)
index 0000000..96540b9
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/sort-down.svg b/resources/assets/icons/sort-down.svg
new file mode 100644 (file)
index 0000000..61fa6c7
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 21.034l6.57-6.554h-4.927V2.966h-3.286V14.48H5.43z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/icons/sort-up.svg b/resources/assets/icons/sort-up.svg
new file mode 100644 (file)
index 0000000..985cc62
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 2.966L5.43 9.52h4.927v11.514h3.286V9.52h4.927z"/><path d="M0 0h24v24H0z" fill="none"/></svg>
\ No newline at end of file
diff --git a/resources/assets/js/components/breadcrumb-listing.js b/resources/assets/js/components/breadcrumb-listing.js
new file mode 100644 (file)
index 0000000..1e2fe9e
--- /dev/null
@@ -0,0 +1,58 @@
+
+
+class BreadcrumbListing {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.searchInput = elem.querySelector('input');
+        this.loadingElem = elem.querySelector('.loading-container');
+        this.entityListElem = elem.querySelector('.breadcrumb-listing-entity-list');
+        this.toggleElem = elem.querySelector('[dropdown-toggle]');
+
+        // this.loadingElem.style.display = 'none';
+        const entityDescriptor = elem.getAttribute('breadcrumb-listing').split(':');
+        this.entityType = entityDescriptor[0];
+        this.entityId = Number(entityDescriptor[1]);
+
+        this.toggleElem.addEventListener('click', this.onShow.bind(this));
+        this.searchInput.addEventListener('input', this.onSearch.bind(this));
+    }
+
+    onShow() {
+        this.loadEntityView();
+    }
+
+    onSearch() {
+        const input = this.searchInput.value.toLowerCase().trim();
+        const listItems = this.entityListElem.querySelectorAll('.entity-list-item');
+        for (let listItem of listItems) {
+            const match = !input || listItem.textContent.toLowerCase().includes(input);
+            listItem.style.display = match ? 'flex' : 'none';
+        }
+    }
+
+    loadEntityView() {
+        this.toggleLoading(true);
+
+        const params = {
+            'entity_id': this.entityId,
+            'entity_type': this.entityType,
+        };
+
+        window.$http.get('/search/entity/siblings', {params}).then(resp => {
+            this.entityListElem.innerHTML = resp.data;
+        }).catch(err => {
+            console.error(err);
+        }).then(() => {
+            this.toggleLoading(false);
+            this.onSearch();
+        });
+    }
+
+    toggleLoading(show = false) {
+        this.loadingElem.style.display = show ? 'block' : 'none';
+    }
+
+}
+
+export default BreadcrumbListing;
\ No newline at end of file
index dda42e868ee6be4d52d761e160d11b624a4fc6bf..400ddb576128bd1f1464700b923d4fc21012c3e5 100644 (file)
@@ -6,7 +6,7 @@ class DropDown {
 
     constructor(elem) {
         this.container = elem;
-        this.menu = elem.querySelector('ul');
+        this.menu = elem.querySelector('ul, [dropdown-menu]');
         this.toggle = elem.querySelector('[dropdown-toggle]');
         this.setupListeners();
     }
index 461bf73217e0567a71b02a0a3645065bc219bf63..58879a20c0c5d8c76534e3af2bfc5a2da59c9e46 100644 (file)
@@ -5,15 +5,17 @@ class EntitySelector {
         this.elem = elem;
         this.search = '';
         this.lastClick = 0;
+        this.selectedItemData = null;
 
-        let entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter';
-        let entityPermission = elem.hasAttribute('entity-permission') ? elem.getAttribute('entity-permission') : 'view';
+        const entityTypes = elem.hasAttribute('entity-types') ? elem.getAttribute('entity-types') : 'page,book,chapter';
+        const entityPermission = elem.hasAttribute('entity-permission') ? elem.getAttribute('entity-permission') : 'view';
         this.searchUrl = window.baseUrl(`/ajax/search/entities?types=${encodeURIComponent(entityTypes)}&permission=${encodeURIComponent(entityPermission)}`);
 
         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.addButton = elem.querySelector('[entity-selector-add-button]');
 
         this.elem.addEventListener('click', this.onClick.bind(this));
 
@@ -26,10 +28,20 @@ class EntitySelector {
                 this.searchEntities(this.searchInput.value);
             }, 200);
         });
+
         this.searchInput.addEventListener('keydown', event => {
             if (event.keyCode === 13) event.preventDefault();
         });
 
+        if (this.addButton) {
+            this.addButton.addEventListener('click', event => {
+                if (this.selectedItemData) {
+                    this.confirmSelection(this.selectedItemData);
+                    this.unselectAll();
+                }
+            });
+        }
+
         this.showLoading();
         this.initialLoad();
     }
@@ -53,7 +65,7 @@ class EntitySelector {
 
     searchEntities(searchTerm) {
         this.input.value = '';
-        let url = this.searchUrl + `&term=${encodeURIComponent(searchTerm)}`;
+        let url = `${this.searchUrl}&term=${encodeURIComponent(searchTerm)}`;
         window.$http.get(url).then(resp => {
             this.resultsContainer.innerHTML = resp.data;
             this.hideLoading();
@@ -68,49 +80,54 @@ class EntitySelector {
     }
 
     onClick(event) {
-        let t = event.target;
-
-        if (t.matches('.entity-list-item  *')) {
+        const listItem = event.target.closest('[data-entity-type]');
+        if (listItem) {
             event.preventDefault();
             event.stopPropagation();
-            let item = t.closest('[data-entity-type]');
-            this.selectItem(item);
-        } else if (t.matches('[data-entity-type]')) {
-            this.selectItem(t)
+            this.selectItem(listItem);
         }
-
     }
 
     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;
+        const isDblClick = this.isDoubleClick();
+        const type = item.getAttribute('data-entity-type');
+        const id = item.getAttribute('data-entity-id');
+        const isSelected = (!item.classList.contains('selected') || isDblClick);
 
         this.unselectAll();
         this.input.value = isSelected ? `${type}:${id}` : '';
 
-        if (!isSelected) window.$events.emit('entity-select-change', null);
+        const link = item.getAttribute('href');
+        const name = item.querySelector('.entity-list-item-name').textContent;
+        const data = {id: Number(id), name: name, link: link};
+
         if (isSelected) {
             item.classList.add('selected');
-            item.classList.add('primary-background');
+            this.selectedItemData = data;
+        } else {
+            window.$events.emit('entity-select-change', null)
         }
+
         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) {
+            this.confirmSelection(data);
+        }
+        if (isSelected) {
+            window.$events.emit('entity-select-change', data)
+        }
+    }
 
-        if (isDblClick) window.$events.emit('entity-select-confirm', data);
-        if (isSelected) window.$events.emit('entity-select-change', data);
+    confirmSelection(data) {
+        window.$events.emit('entity-select-confirm', 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');
+        for (let selectedElem of selected) {
+            selectedElem.classList.remove('selected', 'primary-background');
         }
+        this.selectedItemData = null;
     }
 
 }
index 6f317db62bd3c55259a037647bcf6aed8f8bea8a..a6a38981838e990f3c5de8a3b7e20315a5faf063 100644 (file)
@@ -3,8 +3,13 @@ class ExpandToggle {
 
     constructor(elem) {
         this.elem = elem;
-        this.isOpen = false;
+
+        // Component state
+        this.isOpen = elem.getAttribute('expand-toggle-is-open') === 'yes';
+        this.updateEndpoint = elem.getAttribute('expand-toggle-update-endpoint');
         this.selector = elem.getAttribute('expand-toggle');
+
+        // Listener setup
         elem.addEventListener('click', this.click.bind(this));
     }
 
@@ -53,11 +58,20 @@ class ExpandToggle {
 
     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]);
+
+        const matchingElems = document.querySelectorAll(this.selector);
+        for (let match of matchingElems) {
+            this.isOpen ?  this.close(match) : this.open(match);
         }
+
         this.isOpen = !this.isOpen;
+        this.updateSystemAjax(this.isOpen);
+    }
+
+    updateSystemAjax(isOpen) {
+        window.$http.patch(this.updateEndpoint, {
+            expand: isOpen ? 'true' : 'false'
+        });
     }
 
 }
diff --git a/resources/assets/js/components/header-mobile-toggle.js b/resources/assets/js/components/header-mobile-toggle.js
new file mode 100644 (file)
index 0000000..eccd4b8
--- /dev/null
@@ -0,0 +1,31 @@
+
+class HeaderMobileToggle {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.toggleButton = elem.querySelector('.mobile-menu-toggle');
+        this.menu = elem.querySelector('.header-links');
+        this.open = false;
+
+        this.toggleButton.addEventListener('click', this.onToggle.bind(this));
+        this.onWindowClick = this.onWindowClick.bind(this);
+    }
+
+    onToggle(event) {
+        this.open = !this.open;
+        this.menu.classList.toggle('show', this.open);
+        if (this.open) {
+            window.addEventListener('click', this.onWindowClick)
+        } else {
+            window.removeEventListener('click', this.onWindowClick)
+        }
+        event.stopPropagation();
+    }
+
+    onWindowClick(event) {
+        this.onToggle(event);
+    }
+
+}
+
+module.exports = HeaderMobileToggle;
\ No newline at end of file
index bf6fbf619701baf82059d294487f3c3ad3f4dcaf..355b96473801f47a50d7d12f720c42fd706605b1 100644 (file)
@@ -18,7 +18,11 @@ import toggleSwitch from "./toggle-switch";
 import pageDisplay from "./page-display";
 import shelfSort from "./shelf-sort";
 import homepageControl from "./homepage-control";
-
+import headerMobileToggle from "./header-mobile-toggle";
+import listSortControl from "./list-sort-control";
+import triLayout from "./tri-layout";
+import breadcrumbListing from "./breadcrumb-listing";
+import permissionsTable from "./permissions-table";
 
 const componentMapping = {
     'dropdown': dropdown,
@@ -41,6 +45,11 @@ const componentMapping = {
     'page-display': pageDisplay,
     'shelf-sort': shelfSort,
     'homepage-control': homepageControl,
+    'header-mobile-toggle': headerMobileToggle,
+    'list-sort-control': listSortControl,
+    'tri-layout': triLayout,
+    'breadcrumb-listing': breadcrumbListing,
+    'permissions-table': permissionsTable,
 };
 
 window.components = {};
@@ -79,4 +88,4 @@ function initAll(parentElement) {
 
 window.components.init = initAll;
 
-export default initAll;
\ No newline at end of file
+export default initAll;
diff --git a/resources/assets/js/components/list-sort-control.js b/resources/assets/js/components/list-sort-control.js
new file mode 100644 (file)
index 0000000..d463ed0
--- /dev/null
@@ -0,0 +1,42 @@
+/**
+ * ListSortControl
+ * Manages the logic for the control which provides list sorting options.
+ */
+class ListSortControl {
+
+    constructor(elem) {
+        this.elem = elem;
+
+        this.sortInput = elem.querySelector('[name="sort"]');
+        this.orderInput = elem.querySelector('[name="order"]');
+        this.form = elem.querySelector('form');
+
+        this.elem.addEventListener('click', event => {
+            if (event.target.closest('[data-sort-value]') !== null) {
+                this.sortOptionClick(event);
+            }
+            if (event.target.closest('[data-sort-dir]') !== null) {
+                this.sortDirectionClick(event);
+            }
+        })
+
+    }
+
+    sortOptionClick(event) {
+        const sortOption = event.target.closest('[data-sort-value]');
+        this.sortInput.value = sortOption.getAttribute('data-sort-value');
+        event.preventDefault();
+        this.form.submit();
+    }
+
+    sortDirectionClick(event) {
+        const currentDir = this.orderInput.value;
+        const newDir = (currentDir === 'asc') ? 'desc' : 'asc';
+        this.orderInput.value = newDir;
+        event.preventDefault();
+        this.form.submit();
+    }
+
+}
+
+export default ListSortControl;
\ No newline at end of file
index b8e2bc040c8fe4db7795259cbcbaf515ff3830f1..55cf67813e0d51e156a533e073ca337f94eff1c6 100644 (file)
@@ -64,13 +64,26 @@ class MarkdownEditor {
             let action = button.getAttribute('data-action');
             if (action === 'insertImage') this.actionInsertImage();
             if (action === 'insertLink') this.actionShowLinkSelector();
-            if (action === 'insertDrawing' && event.ctrlKey) {
+            if (action === 'insertDrawing' && (event.ctrlKey || event.metaKey)) {
                 this.actionShowImageManager();
                 return;
             }
             if (action === 'insertDrawing') this.actionStartDrawing();
         });
 
+        // Mobile section toggling
+        this.elem.addEventListener('click', event => {
+            const toolbarLabel = event.target.closest('.editor-toolbar-label');
+            if (!toolbarLabel) return;
+
+            const currentActiveSections = this.elem.querySelectorAll('.markdown-editor-wrap');
+            for (let activeElem of currentActiveSections) {
+                activeElem.classList.remove('active');
+            }
+
+            toolbarLabel.closest('.markdown-editor-wrap').classList.add('active');
+        });
+
         window.$events.listen('editor-markdown-update', value => {
             this.cm.setValue(value);
             this.updateAndRender();
index 2dbf4eb9a87995bbf1b29799383cc329649accd1..1ba5efceadf553e3800f79ed50746d6984014f9f 100644 (file)
@@ -6,7 +6,7 @@ class Overlay {
         elem.addEventListener('click', event => {
              if (event.target === elem) return this.hide();
         });
-        let closeButtons = elem.querySelectorAll('.overlay-close');
+        let closeButtons = elem.querySelectorAll('.popup-header-close');
         for (let i=0; i < closeButtons.length; i++) {
             closeButtons[i].addEventListener('click', this.hide.bind(this));
         }
index c86b90d2ca43b7ba108d28979f13ed663d0e981d..975ff5a824b080bb5924ea46d557b1167103d364 100644 (file)
@@ -54,7 +54,7 @@ class PageComments {
         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';
+        textArea.style.height = ((lineCount * 20) + 40) + 'px';
         this.editingComment = commentElem;
     }
 
@@ -88,6 +88,7 @@ class PageComments {
             commentElem.parentNode.removeChild(commentElem);
             window.$events.emit('success', window.trans('entities.comment_deleted_success'));
             this.updateCount();
+            this.hideForm();
         });
     }
 
@@ -129,7 +130,7 @@ class PageComments {
     showForm() {
         this.formContainer.style.display = 'block';
         this.formContainer.parentNode.style.display = 'block';
-        this.elem.querySelector('[comment-add-button]').style.display = 'none';
+        this.elem.querySelector('[comment-add-button-container]').style.display = 'none';
         this.formInput.focus();
         window.scrollToElement(this.formInput);
     }
@@ -137,7 +138,18 @@ class PageComments {
     hideForm() {
         this.formContainer.style.display = 'none';
         this.formContainer.parentNode.style.display = 'none';
-        this.elem.querySelector('[comment-add-button]').style.display = 'block';
+        const addButtonContainer = this.elem.querySelector('[comment-add-button-container]');
+        if (this.getCommentCount() > 0) {
+            this.elem.appendChild(addButtonContainer)
+        } else {
+            const countBar = this.elem.querySelector('[comment-count-bar]');
+            countBar.appendChild(addButtonContainer);
+        }
+        addButtonContainer.style.display = 'block';
+    }
+
+    getCommentCount() {
+        return this.elem.querySelectorAll('.comment-box[comment]').length;
     }
 
     setReply(commentElem) {
index bca641cb6161e65e77b10bf81630bfe95c24e359..1aeeaf248a6df307ee342d9804629447d6639b1d 100644 (file)
@@ -208,8 +208,8 @@ class PageDisplay {
             let pageNavObserver = new IntersectionObserver(headingVisibilityChange, intersectOpts);
 
             // observe each heading
-            for (let i = 0; i !== headings.length; ++i) {
-                pageNavObserver.observe(headings[i]);
+            for (let heading of headings) {
+                pageNavObserver.observe(heading);
             }
         }
 
@@ -221,14 +221,9 @@ class PageDisplay {
         }
 
         function toggleAnchorHighlighting(elementId, shouldHighlight) {
-            let anchorsToHighlight = pageNav.querySelectorAll('a[href="#' + elementId + '"]');
-            for (let i = 0; i < anchorsToHighlight.length; i++) {
-                // Change below to use classList.toggle when IE support is dropped.
-                if (shouldHighlight) {
-                    anchorsToHighlight[i].classList.add('current-heading');
-                } else {
-                    anchorsToHighlight[i].classList.remove('current-heading');
-                }
+            const anchorsToHighlight = pageNav.querySelectorAll('a[href="#' + elementId + '"]');
+            for (let anchor of anchorsToHighlight) {
+                anchor.closest('li').classList.toggle('current-heading', shouldHighlight);
             }
         }
     }
diff --git a/resources/assets/js/components/permissions-table.js b/resources/assets/js/components/permissions-table.js
new file mode 100644 (file)
index 0000000..baad752
--- /dev/null
@@ -0,0 +1,66 @@
+
+class PermissionsTable {
+
+    constructor(elem) {
+        this.container = elem;
+
+        // Handle toggle all event
+        const toggleAll = elem.querySelector('[permissions-table-toggle-all]');
+        toggleAll.addEventListener('click', this.toggleAllClick.bind(this));
+
+        // Handle toggle row event
+        const toggleRowElems = elem.querySelectorAll('[permissions-table-toggle-all-in-row]');
+        for (let toggleRowElem of toggleRowElems) {
+            toggleRowElem.addEventListener('click', this.toggleRowClick.bind(this));
+        }
+
+        // Handle toggle column event
+        const toggleColumnElems = elem.querySelectorAll('[permissions-table-toggle-all-in-column]');
+        for (let toggleColElem of toggleColumnElems) {
+            toggleColElem.addEventListener('click', this.toggleColumnClick.bind(this));
+        }
+    }
+
+    toggleAllClick(event) {
+        event.preventDefault();
+        this.toggleAllInElement(this.container);
+    }
+
+    toggleRowClick(event) {
+        event.preventDefault();
+        this.toggleAllInElement(event.target.closest('tr'));
+    }
+
+    toggleColumnClick(event) {
+        event.preventDefault();
+
+        const tableCell = event.target.closest('th,td');
+        const colIndex = Array.from(tableCell.parentElement.children).indexOf(tableCell);
+        const tableRows = tableCell.closest('table').querySelectorAll('tr');
+        const inputsToToggle = [];
+
+        for (let row of tableRows) {
+            const targetCell = row.children[colIndex];
+            if (targetCell) {
+                inputsToToggle.push(...targetCell.querySelectorAll('input[type=checkbox]'));
+            }
+        }
+        this.toggleAllInputs(inputsToToggle);
+    }
+
+    toggleAllInElement(domElem) {
+        const inputsToToggle = domElem.querySelectorAll('input[type=checkbox]');
+        this.toggleAllInputs(inputsToToggle);
+    }
+
+    toggleAllInputs(inputsToToggle) {
+        const currentState = inputsToToggle.length > 0 ? inputsToToggle[0].checked : false;
+        for (let checkbox of inputsToToggle) {
+            checkbox.checked = !currentState;
+            checkbox.dispatchEvent(new Event('change'));
+        }
+    }
+
+}
+
+export default PermissionsTable;
\ No newline at end of file
index 957a41642141c398b3016110eb484d05e382ac84..3be67d5dc94afb0f0f10ec6788c6590541ab04f0 100644 (file)
@@ -3,15 +3,15 @@ class ToggleSwitch {
 
     constructor(elem) {
         this.elem = elem;
-        this.input = elem.querySelector('input');
+        this.input = elem.querySelector('input[type=hidden]');
+        this.checkbox = elem.querySelector('input[type=checkbox]');
 
-        this.elem.onclick = this.onClick.bind(this);
+        this.checkbox.addEventListener('change', this.onClick.bind(this));
     }
 
     onClick(event) {
-        let checked = this.input.value !== 'true';
+        let checked = this.checkbox.checked;
         this.input.value = checked ? 'true' : 'false';
-        checked ? this.elem.classList.add('active') : this.elem.classList.remove('active');
     }
 
 }
diff --git a/resources/assets/js/components/tri-layout.js b/resources/assets/js/components/tri-layout.js
new file mode 100644 (file)
index 0000000..0ae7df9
--- /dev/null
@@ -0,0 +1,95 @@
+
+class TriLayout {
+
+    constructor(elem) {
+        this.elem = elem;
+
+        this.lastLayoutType = 'none';
+        this.onDestroy = null;
+        this.scrollCache = {
+            'content': 0,
+            'info': 0,
+        };
+        this.lastTabShown = 'content';
+
+        // Bind any listeners
+        this.mobileTabClick = this.mobileTabClick.bind(this);
+
+        // Watch layout changes
+        this.updateLayout();
+        window.addEventListener('resize', event => {
+            this.updateLayout();
+        }, {passive: true});
+    }
+
+    updateLayout() {
+        let newLayout = 'tablet';
+        if (window.innerWidth <= 1000) newLayout =  'mobile';
+        if (window.innerWidth >= 1400) newLayout =  'desktop';
+        if (newLayout === this.lastLayoutType) return;
+
+        if (this.onDestroy) {
+            this.onDestroy();
+            this.onDestroy = null;
+        }
+
+        if (newLayout === 'desktop') {
+            this.setupDesktop();
+        } else if (newLayout === 'mobile') {
+            this.setupMobile();
+        }
+
+        this.lastLayoutType = newLayout;
+    }
+
+    setupMobile() {
+        const layoutTabs = document.querySelectorAll('[tri-layout-mobile-tab]');
+        for (let tab of layoutTabs) {
+            tab.addEventListener('click', this.mobileTabClick);
+        }
+
+        this.onDestroy = () => {
+            for (let tab of layoutTabs) {
+                tab.removeEventListener('click', this.mobileTabClick);
+            }
+        }
+    }
+
+    setupDesktop() {
+        //
+    }
+
+
+    /**
+     * Action to run when the mobile info toggle bar is clicked/tapped
+     * @param event
+     */
+    mobileTabClick(event) {
+        const tab = event.target.getAttribute('tri-layout-mobile-tab');
+        this.scrollCache[this.lastTabShown] = document.documentElement.scrollTop;
+
+        // Set tab status
+        const activeTabs = document.querySelectorAll('.tri-layout-mobile-tab.active');
+        for (let tab of activeTabs) {
+            tab.classList.remove('active');
+        }
+        event.target.classList.add('active');
+
+        // Toggle section
+        const showInfo = (tab === 'info');
+        this.elem.classList.toggle('show-info', showInfo);
+
+        // Set the scroll position from cache
+        const pageHeader = document.querySelector('header');
+        const defaultScrollTop = pageHeader.getBoundingClientRect().bottom;
+        document.documentElement.scrollTop = this.scrollCache[tab] || defaultScrollTop;
+        setTimeout(() => {
+            document.documentElement.scrollTop = this.scrollCache[tab] || defaultScrollTop;
+        }, 50);
+
+        this.lastTabShown = tab;
+    }
+
+}
+
+export default TriLayout;
\ No newline at end of file
index 9deb1d0a7a0f4e2a9c16945d919fe6e9b61148ff..ce5cfbf4e66c9fe990cb2fe86bc21d8a5c0f2214 100644 (file)
@@ -432,7 +432,7 @@ class WysiwygEditor {
             plugins: this.plugins,
             imagetools_toolbar: 'imageoptions',
             toolbar: this.getToolBar(),
-            content_style: "body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
+            content_style: "html, body {background: #FFF;} body {padding-left: 15px !important; padding-right: 15px !important; margin:0!important; margin-left:auto!important;margin-right:auto!important;}",
             style_formats: [
                 {title: "Header Large", format: "h2"},
                 {title: "Header Medium", format: "h3"},
@@ -517,6 +517,16 @@ class WysiwygEditor {
                     if (scrollId) {
                         scrollToText(scrollId);
                     }
+
+                    // Override for touch events to allow scroll on mobile
+                    const container = editor.getContainer();
+                    const toolbarButtons = container.querySelectorAll('.mce-btn');
+                    for (let button of toolbarButtons) {
+                        button.addEventListener('touchstart', event => {
+                            event.stopPropagation();
+                        });
+                    }
+                    window.editor = editor;
                 });
 
                 function editorChange() {
@@ -600,6 +610,7 @@ class WysiwygEditor {
 
                 // Paste image-uploads
                 editor.on('paste', event => editorPaste(event, editor, context));
+
             }
         };
     }
index c03553d15bf196d09a6702185c63853b14b11ba0..85fd96206393a64581c08b829587d01cc09b3e06 100644 (file)
   }
 }
 
-.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 {
-  from {
-    opacity: 0;
-    transform: scale3d(0, 0, 1);
-  }
-
-  to {
-    opacity: 1;
-    transform: scale3d(1, 1, 1);
-  }
-}
-
 @keyframes loadingBob {
   0% {
     transform: translate3d(0, 0, 0);
@@ -89,8 +69,4 @@
   animation-duration: 180ms;
   animation-delay: 0s;
   animation-timing-function: cubic-bezier(.62, .28, .23, .99);
-}
-
-.selectFade {
-  transition: background-color ease-in-out 3000ms;
 }
\ No newline at end of file
index c0f02ed7d78f34b48908f02d2ca7be84476e12c6..efdefc016fbdc3dc6196f9641f09af21251a4e77 100644 (file)
@@ -1,136 +1,7 @@
 
-/*
-* This file container all block styling including background shading,
-* margins, paddings & borders.
-*/
-
-
-/*
-* Background Shading
-*/
-.shaded {
-  background-color: #f1f1f1;
-  &.pos {
-    background-color: lighten($positive, 40%);
-  }
-  &.neg {
-    background-color: lighten($negative, 20%);
-  }
-  &.primary {
-    background-color: lighten($primary, 40%);
-  }
-  &.secondary {
-    background-color: lighten($secondary, 30%);
-  }
-}
-
-/*
-* Bordering
-*/
-.bordered {
-  border: 1px solid #BBB;
-  &.pos {
-    border-color: $positive;
-  }
-  &.neg {
-    border-color: $negative;
-  }
-  &.primary {
-    border-color: $primary;
-  }
-  &.secondary {
-    border-color: $secondary;
-  }
-  &.thick {
-    border-width: 2px;
-  }
-}
-.rounded {
-  border-radius: 3px;
-}
-
-/*
-* Padding
-*/
-.nopadding {
-  padding: 0;
-}
-.padded {
-  padding: $-l;
-  &.large {
-    padding: $-xl;
-  }
-  >h1, >h2, >h3, >h4 {
-    &:first-child {
-      margin-top: 0.1em;
-    }
-  }
-}
-.padded-vertical, .padded-top {
-  padding-top: $-m;
-  &.large {
-    padding-top: $-xl;
-  }
-}
-
-.padded-vertical, .padded-bottom {
-  padding-bottom: $-m;
-  &.large {
-    padding-bottom: $-xl;
-  }
-}
-.padded-horizontal, .padded-left {
-  padding-left: $-m;
-  &.large {
-    padding-left: $-xl;
-  }
-}
-.padded-horizontal, .padded-right {
-  padding-right: $-m;
-  &.large {
-    padding-right: $-xl;
-  }
-}
-
-/*
-* Margins
-*/
-.margins {
-  margin: $-l;
-  &.large {
-    margin: $-xl;
-  }
-}
-.margins-vertical, .margin-top {
-  margin-top: $-m;
-  &.large {
-    margin-top: $-xl;
-  }
-}
-.margins-vertical, .margin-bottom {
-  margin-bottom: $-m;
-  &.large {
-    margin-bottom: $-xl;
-  }
-}
-.margins-horizontal, .margin-left {
-  margin-left: $-m;
-  &.large {
-    margin-left: $-xl;
-  }
-}
-.margins-horizontal, .margin-right {
-  margin-right: $-m;
-  &.large {
-    margin-right: $-xl;
-  }
-}
-
-
 /**
  * Callouts
  */
-
 .callout {
   border-left: 3px solid #BBB;
   background-color: #EEE;
   }
 }
 
+/**
+ * Card-style blocks
+ */
+
 .card {
-  margin: $-m;
   background-color: #FFF;
-  box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.2);
+  box-shadow: $bs-card;
+  border-radius: 3px;
+  border: 1px solid transparent;
   h3 {
-    padding: $-m;
-    border-bottom: 1px solid #E8E8E8;
+    padding: $-m $-m $-xs;
     margin: 0;
-    font-size: $fs-s;
-    color: #888;
-    fill: #888;
+    font-size: $fs-m;
+    color: #222;
+    fill: #222;
     font-weight: 400;
-    text-transform: uppercase;
   }
   h3 a {
     line-height: 1;
   }
 }
 
-.sidebar .card {
-  h3, .body, .empty-text {
-    padding: $-s $-m;
-  }
-}
-
 .card.drag-card {
   border: 1px solid #DDD;
   border-radius: 4px;
   display: flex;
-  padding: 0;
-  padding-left: $-s + 28px;
+  padding: 0 0 0 ($-s + 28px);
   margin: $-s 0;
   position: relative;
   .drag-card-action {
   }
   .handle, .drag-card-action {
     display: flex;
-    padding: 0;
     align-items: center;
     text-align: center;
     justify-content: center;
     width: 28px;
     flex-grow: 0;
-    padding-left: $-xs;
-    padding-right: $-xs;
+    padding: 0 $-xs;
     &:hover {
       background-color: #EEE;
     }
     margin: $-s 0;
     width: 100%;
   }
-  > div.padded {
-    padding: $-s 0 !important;
-  }
   .handle {
     background-color: #EEE;
     left: 0;
   }
 }
 
-.well {
-  background-color: #F8F8F8;
-  padding: $-m;
-  border: 1px solid #DDD;
+.grid-card {
+  display: flex;
+  flex-direction: column;
+  border: 1px solid #ddd;
+  margin-bottom: $-l;
+  border-radius: 4px;
+  overflow: hidden;
+  min-width: 100px;
+  color: $text-dark;
+  transition: border-color ease-in-out 120ms, box-shadow ease-in-out 120ms;
+  &:hover {
+    color: $text-dark;
+    text-decoration: none;
+    box-shadow: $bs-card;
+  }
+  h2 {
+    width: 100%;
+    font-size: 1.5em;
+    margin: 0 0 10px;
+  }
+  p {
+    font-size: .7rem;
+    margin: 0;
+    line-height: 1.6em;
+  }
+  .grid-card-content {
+    flex: 1;
+    border-top: 0;
+    border-bottom-width: 2px;
+  }
+  .grid-card-content, .grid-card-footer {
+    padding: $-l;
+  }
+  .grid-card-content + .grid-card-footer {
+    padding-top: 0;
+  }
+}
+
+.bookshelf-grid-item .grid-card-content h2 a  {
+  color: $color-bookshelf;
+  fill: $color-bookshelf;
 }
 
+.book-grid-item .grid-card-footer {
+  p.small {
+    font-size: .8em;
+    margin: 0;
+  }
+}
+
+.content-wrap.card {
+  padding: $-m $-xxl;
+  margin-left: auto;
+  margin-right: auto;
+  margin-bottom: $-xl;
+  overflow: auto;
+  min-height: 60vh;
+  &.auto-height {
+    min-height: 0;
+  }
+  &.fill-width {
+    width: 100%;
+  }
+}
+@include smaller-than($xxl) {
+  .content-wrap.card {
+    padding: $-l $-xl;
+  }
+}
+@include smaller-than($m) {
+  .content-wrap.card {
+    padding: $-m $-l;
+  }
+}
+@include smaller-than($s) {
+  .content-wrap.card {
+    padding: $-m $-s;
+  }
+}
+
+/**
+ * Tags
+ */
 .tag-item {
   display: inline-flex;
   margin-bottom: $-xs;
index 2c20c3f41a50eba38ef7b4a957cde2d6140d4f7d..eb7a09342ad60079503534da6294393af6a75fbc 100644 (file)
@@ -1,15 +1,14 @@
+button {
+  font-size: 100%;
+}
 
 @mixin generate-button-colors($textColor, $backgroundColor) {
   background-color: $backgroundColor;
   color: $textColor;
   fill: $textColor;
-  text-transform: uppercase;
   border: 1px solid $backgroundColor;
-  vertical-align: top;
   &:hover {
     background-color: lighten($backgroundColor, 8%);
-    //box-shadow: $bs-med;
-    text-decoration: none;
     color: $textColor;
   }
   &:active {
@@ -18,7 +17,6 @@
   &:focus {
     background-color: lighten($backgroundColor, 4%);
     box-shadow: $bs-light;
-    text-decoration: none;
     color: $textColor;
   }
 }
 // Button Specific Variables
 $button-border-radius: 2px;
 
-.button-base {
+.button  {
   text-decoration: none;
-  font-size: $fs-m;
+  font-size: 0.85rem;
   line-height: 1.4em;
   padding: $-xs*1.3 $-m;
-  margin: $-xs $-xs $-xs 0;
+  margin-top: $-xs;
+  margin-bottom: $-xs;
   display: inline-block;
-  border: none;
   font-weight: 400;
   outline: 0;
   border-radius: $button-border-radius;
   cursor: pointer;
-  transition: all ease-in-out 120ms;
-  box-shadow: 0;
-  @include generate-button-colors(#EEE, $primary);
-}
-
-.button, input[type="button"], input[type="submit"]  {
-  @extend .button-base;
-  &.pos {
-    @include generate-button-colors(#EEE, $positive);
-  }
-  &.neg {
-    @include generate-button-colors(#EEE, $negative);
-  }
-  &.secondary {
-    @include generate-button-colors(#EEE, $secondary);
-  }
-  &.muted {
-    @include generate-button-colors(#EEE, #AAA);
+  transition: background-color ease-in-out 120ms, box-shadow ease-in-out 120ms;
+  box-shadow: none;
+  background-color: $primary;
+  color: #FFF;
+  fill: #FFF;
+  text-transform: uppercase;
+  border: 1px solid $primary;
+  vertical-align: top;
+  &:hover, &:focus {
+    text-decoration: none;
   }
-  &.muted-light {
-    @include generate-button-colors(#666, #e4e4e4);
+  &:active {
+    background-color: darken($primary, 8%);
   }
 }
-
+.button.primary {
+  @include generate-button-colors(#FFFFFF, $primary);
+}
 .button.outline {
   background-color: transparent;
   color: #888;
@@ -71,78 +63,38 @@ $button-border-radius: 2px;
     box-shadow: none;
     background-color: #EEE;
   }
-  &.page {
-    border-color: $color-page;
-    color: $color-page;
-    fill: $color-page;
-    &:hover, &:focus, &:active {
-      background-color: $color-page;
-      color: #FFF;
-      fill: #FFF;
-    }
-  }
-  &.chapter {
-    border-color: $color-chapter;
-    color: $color-chapter;
-    fill: $color-chapter;
-    &:hover, &:focus, &:active {
-      background-color: $color-chapter;
-      color: #FFF;
-      fill: #FFF;
-    }
-  }
-  &.book {
-    border-color: $color-book;
-    color: $color-book;
-    fill: $color-book;
-    &:hover, &:focus, &:active {
-      background-color: $color-book;
-      color: #FFF;
-      fill: #FFF;
-    }
-  }
+}
+
+.button + .button {
+  margin-left: $-s;
+}
+
+.button.small {
+  font-size: 0.75rem;
+  padding: $-xs*1.2 $-s;
 }
 
 .text-button {
-  @extend .link;
+  cursor: pointer;
   background-color: transparent;
   padding: 0;
   margin: 0;
   border: none;
   user-select: none;
+  font-size: 0.75rem;
+  line-height: 1.4em;
   &:focus, &:active {
     outline: 0;
   }
   &:hover {
     text-decoration: none;
   }
-  &.neg {
-    color: $negative;
-  }
-}
-
-.button-group {
-  @include clearfix;
-  .button, button[type="button"] {
-    margin: $-xs 0 $-xs 0;
-    float: left;
-    border-radius: 0;
-    &:first-child {
-      border-radius: $button-border-radius 0 0 $button-border-radius;
-    }
-    &:last-child {
-      border-radius: 0 $button-border-radius $button-border-radius 0;
-    }
-  }
 }
 
 .button.block {
   width: 100%;
-  text-align: center;
+  text-align: left;
   display: block;
-  &.text-left {
-    text-align: left;
-  }
 }
 
 .button.icon {
@@ -160,9 +112,7 @@ $button-border-radius: 2px;
     width: 24px;
     height: 24px;
   }
-  padding: $-s $-m;
-  padding-bottom: $-s - 2px;
-  padding-left: $-m*2 + 24px;
+  padding: $-s $-m ($-s - 2px) ($-m*2 + 24px);
 }
 
 .button[disabled] {
diff --git a/resources/assets/sass/_colors.scss b/resources/assets/sass/_colors.scss
new file mode 100644 (file)
index 0000000..4dfc9d4
--- /dev/null
@@ -0,0 +1,72 @@
+
+/*
+ * Status text colors
+ */
+.text-pos, .text-pos:hover, .text-pos-hover:hover {
+  color: $positive !important;
+  fill: $positive !important;
+}
+
+.text-warn, .text-warn:hover, .text-warn-hover:hover {
+  color: $warning !important;
+  fill: $warning !important;
+}
+
+.text-neg, .text-neg:hover, .text-neg-hover:hover  {
+  color: $negative !important;
+  fill: $negative !important;
+}
+
+/*
+ * Style text colors
+ */
+.text-primary, .text-primary:hover, .text-primary-hover:hover  {
+  color: $primary !important;
+  fill: $primary !important;
+}
+
+.text-muted {
+  color: lighten($text-dark, 26%) !important;
+  fill: lighten($text-dark, 26%) !important;
+  &.small, .small {
+    color: lighten($text-dark, 32%) !important;
+    fill: lighten($text-dark, 32%) !important;
+  }
+}
+
+/*
+ * Entity text colors
+ */
+.text-bookshelf, .text-bookshelf:hover {
+  color: $color-bookshelf;
+  fill: $color-bookshelf;
+}
+.text-book, .text-book:hover {
+  color: $color-book;
+  fill: $color-book;
+}
+.text-page, .text-page:hover {
+  color: $color-page;
+  fill: $color-page;
+}
+.text-page.draft, .text-page.draft:hover {
+  color: $color-page-draft;
+  fill: $color-page-draft;
+}
+.text-chapter, .text-chapter:hover {
+  color: $color-chapter;
+  fill: $color-chapter;
+}
+
+/*
+ * Entity background colors
+ */
+.bg-book {
+  background-color: $color-book;
+}
+.bg-chapter {
+  background-color: $color-chapter;
+}
+.bg-shelf {
+  background-color: $color-bookshelf;
+}
\ No newline at end of file
index 1f34166c61ffda83e9f72434bdf0875c6d2804f6..33bc5e71de38c2f1c86c0afbd278b05ef77b8a0d 100644 (file)
   transition: all ease-in-out 180ms;
   user-select: none;
   svg[data-icon="caret-right"] {
+    margin-right: 0;
+    font-size: 1rem;
     transition: all ease-in-out 180ms;
     transform: rotate(0deg);
-    transform-origin: 25% 50%;
+    transform-origin: 50% 50%;
   }
   &.open svg[data-icon="caret-right"] {
     transform: rotate(90deg);
   }
+  svg[data-icon="caret-right"] + * {
+    margin-left: $-xs;
+  }
 }
 
 [overlay] {
   }
 }
 
-.corner-button {
+.popup-footer button, .popup-header-close {
   position: absolute;
   top: 0;
   right: 0;
   height: 40px;
   border-radius: 0;
   box-shadow: none;
+  &:active {
+    outline: 0;
+  }
+}
+.popup-header-close {
+  background-color: transparent;
+  border: 0;
+  color: #FFF;
+  font-size: 16px;
+  padding: 0 $-m;
 }
 
 .popup-header, .popup-footer {
     padding: 8px $-m;
   }
 }
+.popup-footer {
+  margin-top: 1px;
+}
 body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   height: 444px;
   min-height: 444px;
@@ -137,6 +155,9 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 #entity-selector-wrap .popup-body .form-group {
   margin: 0;
 }
+.popup-body .entity-selector-container {
+  flex: 1;
+}
 
 .image-manager-body {
   min-height: 70vh;
@@ -583,27 +604,26 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 }
 
 .comment-box {
-  clear: left;
   border: 1px solid #DDD;
-  margin-bottom: $-s;
-  border-radius: 3px;
+  border-radius: 4px;
+  background-color: #FFF;
   .content {
-    padding: $-s;
     font-size: 0.666em;
     p, ul, ol {
       font-size: $fs-m;
       margin: .5em 0;
     }
   }
-  .reply-row {
-    padding: $-xs $-s;
+  .actions {
+    opacity: 0;
+    transition: opacity ease-in-out 120ms;
+  }
+  &:hover .actions {
+    opacity: 1;
   }
 }
 
 .comment-box .header {
-  padding: $-xs $-s;
-  background-color: #f8f8f8;
-  border-bottom: 1px solid #DDD;
   .meta {
     img, a, span {
       display: inline-block;
@@ -626,4 +646,11 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 
 #tag-manager .drag-card {
   max-width: 500px;
+}
+
+.permissions-table [permissions-table-toggle-all-in-row] {
+  display: none;
+}
+.permissions-table tr:hover [permissions-table-toggle-all-in-row] {
+  display: inline;
 }
\ No newline at end of file
index 6b3ed381549e3c1c8f3dedfb99b2a1ba06b09566..f40c92a194d6ca4b636ee871b78d6c5adbb4a4ec 100644 (file)
   }
 }
 
+@include smaller-than($m) {
+  #markdown-editor {
+    flex-direction: column;
+  }
+  #markdown-editor .markdown-editor-wrap {
+    width: 100%;
+    max-width: 100%;
+  }
+  #markdown-editor .editor-toolbar {
+    padding: 0;
+  }
+  #markdown-editor .editor-toolbar > * {
+    padding: $-xs $-s;
+  }
+  .editor-toolbar-label {
+    float: none !important;
+    border-bottom: 1px solid #DDD;
+    display: block;
+  }
+  .markdown-editor-wrap:not(.active) .editor-toolbar + div, .markdown-editor-wrap:not(.active) .editor-toolbar .buttons {
+    display: none;
+  }
+  #markdown-editor .markdown-editor-wrap:not(.active) {
+    flex-grow: 0;
+    flex: none;
+  }
+}
+
 .markdown-display {
   padding: 0 $-m 0;
   margin-left: -1px;
@@ -98,7 +126,7 @@ label {
   line-height: 1.4em;
   font-size: 0.94em;
   font-weight: 400;
-  color: #999;
+  color: #666;
   padding-bottom: 2px;
   margin-bottom: 0.2em;
   &.inline {
@@ -139,56 +167,77 @@ input[type=date] {
 }
 
 .toggle-switch {
-  display: inline-block;
-  background-color: #BBB;
-  width: 36px;
-  height: 14px;
-  border-radius: 7px;
-  position: relative;
-  transition: all ease-in-out 120ms;
-  cursor: pointer;
   user-select: none;
-  &:after {
-    content: '';
-    display: block;
-    position: relative;
-    left: 0;
-    margin-top: -3px;
-    width: 20px;
-    height: 20px;
-    border-radius: 50%;
-    background-color: #fafafa;
-    border: 1px solid #CCC;
-    box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);
-    transition: all ease-in-out 120ms;
-  }
-  &.active {
-    background-color: rgba($positive, 0.4);
-    &:after {
-      left: 16px;
-      background-color: $positive;
-      border: darken($positive, 20%);
+  display: inline-grid;
+  grid-template-columns: (16px + $-s) 1fr;
+  align-items: center;
+  margin: $-m 0;
+  .custom-checkbox {
+    width: 16px;
+    height: 16px;
+    border-radius: 2px;
+    display: inline-block;
+    border: 2px solid currentColor;
+    opacity: 0.6;
+    overflow: hidden;
+    fill: currentColor;
+    .svg-icon {
+      width: 100%;
+      height: 100%;
+      margin: 0;
+      bottom: auto;
+      top: -1.5px;
+      left: 0;
+      transition: transform ease-in-out 120ms;
+      transform: scale(0);
+      transform-origin: center center;
     }
   }
+  input[type=checkbox] {
+    display: none;
+  }
+  input[type=checkbox]:checked + .custom-checkbox .svg-icon {
+    transform: scale(1);
+  }
+  .custom-checkbox:hover {
+    background-color: rgba(0, 0, 0, 0.05);
+    opacity: 0.8;
+  }
 }
-.toggle-switch-checkbox {
-  display: none;
-}
-input:checked + .toggle-switch {
-  background-color: rgba($positive, 0.4);
-  &:after {
-    left: 16px;
-    background-color: $positive;
-    border: darken($positive, 20%);
+.toggle-switch-list {
+  .toggle-switch {
+    margin: $-xs 0;
+  }
+  &.compact .toggle-switch {
+    margin: 1px 0;
   }
 }
 
 .form-group {
   margin-bottom: $-s;
-  textarea {
-    display: block;
+}
+
+.setting-list > div {
+  border-bottom: 1px solid #DDD;
+  padding: $-xl 0;
+  &:last-child {
+    border-bottom: none;
+  }
+}
+.setting-list-label {
+  color: #222;
+  font-size: 1rem;
+}
+.setting-list-label + p.small {
+  margin-bottom: 0;
+}
+.setting-list-label + .grid {
+  margin-top: $-m;
+}
+
+.setting-list .grid, .stretch-inputs {
+  input[type=text], input[type=email], input[type=password], select {
     width: 100%;
-    min-height: 64px;
   }
 }
 
@@ -197,6 +246,8 @@ input:checked + .toggle-switch {
   font-family: monospace;
   font-size: 12px;
   min-height: 100px;
+  display: block;
+  width: 100%;
 }
 
 .form-group {
@@ -206,11 +257,9 @@ input:checked + .toggle-switch {
 }
 
 .form-group[collapsible] {
-  margin-left: -$-m;
-  margin-right: -$-m;
   padding: 0 $-m;
-  border-top: 1px solid #DDD;
-  border-bottom: 1px solid #DDD;
+  border: 1px solid #DDD;
+  border-radius: 4px;
   .collapse-title {
     margin-left: -$-m;
     margin-right: -$-m;
@@ -238,9 +287,6 @@ input:checked + .toggle-switch {
   &.open .collapse-title label:before {
     transform: rotate(90deg);
   }
-  &+.form-group[collapsible] {
-    margin-top: -($-s + 1);
-  }
 }
 
 .inline-input-style {
@@ -304,6 +350,13 @@ div[editor-type="markdown"] .title-input.page-title input[type="text"] {
     width: 300px;
     max-width: 100%;
   }
+  &.flexible input {
+    width: 100%;
+  }
+  .search-box-cancel {
+    left: auto;
+    right: 0;
+  }
 }
 
 .outline > input {
@@ -317,13 +370,6 @@ div[editor-type="markdown"] .title-input.page-title input[type="text"] {
   }
 }
 
-#login-form label[for="remember"] {
-  margin: 0;
-}
-#login-form label.toggle-switch {
-  margin-left: $-xl;
-}
-
 .image-picker img {
   background-color: #BBB;
 }
diff --git a/resources/assets/sass/_grid.scss b/resources/assets/sass/_grid.scss
deleted file mode 100644 (file)
index 0e1f85c..0000000
+++ /dev/null
@@ -1,930 +0,0 @@
-
-/** Flexbox styling rules **/
-body.flexbox {
-  display: flex;
-  flex-direction: column;
-  align-items: stretch;
-  height: 100%;
-  min-height: 100%;
-  max-height: 100%;
-  overflow: hidden;
-  #content {
-    flex: 1;
-    display: flex;
-    min-height: 0;
-  }
-}
-
-.flex-fill {
-  display: flex;
-  align-items: stretch;
-  min-height: 0;
-  max-width: 100%;
-  position: relative;
-  &.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;
-}
-
-.flex.sidebar {
-  flex: 1;
-  background-color: #F2F2F2;
-  max-width: 360px;
-  min-height: 90vh;
-  section {
-    margin: $-m;
-  }
-}
-.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;
-    fill: #666;
-    font-size: 20px;
-    vertical-align: middle;
-    text-align: center;
-    border: 1px solid #DDD;
-    border-top: 1px solid #BBB;
-    padding-top: $-m;
-    cursor: pointer;
-    svg {
-      opacity: 0.5;
-      transition: all ease-in-out 120ms;
-      margin: 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: auto;
-    -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 {
-  max-width: 100%;
-}
-
-.container {
-  max-width: $max-width;
-  margin-left: auto;
-  margin-right: auto;
-  padding-left: $-m;
-  padding-right: $-m;
-  &.fluid {
-    max-width: 100%;
-  }
-  &.medium {
-    max-width: 992px;
-  }
-  &.small {
-    max-width: 840px;
-  }
-  &.nopad {
-    padding-left: 0;
-    padding-right: 0;
-  }
-}
-
-.row {
-  margin-left: -$-m;
-  margin-right: -$-m;
-}
-
-.grid {
-  display: grid;
-  grid-column-gap: $-l;
-  grid-row-gap: $-l;
-  &.third {
-    grid-template-columns: 1fr 1fr 1fr;
-  }
-}
-
-.grid-card {
-  display: flex;
-  flex-direction: column;
-  border: 1px solid #ddd;
-  min-width: 100px;
-  h2 {
-    width: 100%;
-    font-size: 1.5em;
-    margin: 0 0 10px;
-  }
-  h2 a {
-    display: block;
-    width: 100%;
-    line-height: 1.2;
-    text-decoration: none;
-  }
-  p {
-    font-size: .85em;
-    margin: 0;
-    line-height: 1.6em;
-  }
-  .grid-card-content {
-    flex: 1;
-    border-top: 0;
-    border-bottom-width: 2px;
-  }
-  .grid-card-content, .grid-card-footer {
-    padding: $-l;
-  }
-  .grid-card-content + .grid-card-footer {
-    padding-top: 0;
-  }
-}
-
-.book-grid-item .grid-card-content h2 a  {
-    color: $color-book;
-    fill: $color-book;
-}
-
-.bookshelf-grid-item .grid-card-content h2 a  {
-  color: $color-bookshelf;
-  fill: $color-bookshelf;
-}
-
-.book-grid-item .grid-card-footer {
-  p.small {
-    font-size: .8em;
-    margin: 0;
-  }
-}
-
-@include smaller-than($m) {
-  .grid.third {
-    grid-template-columns: 1fr 1fr;
-  }
-}
-
-@include smaller-than($s) {
-  .grid.third {
-    grid-template-columns: 1fr;
-  }
-}
-
-.float {
-  float: left;
-  &.right {
-    float: right;
-  }
-}
-
-.block {
-  display: block;
-  position: relative;
-}
-
-.inline {
-  display: inline;
-}
-
-.block.inline {
-  display: inline-block;
-}
-
-.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
-  position: relative;
-  min-height: 1px;
-  padding-left: $-m;
-  padding-right: $-m;
-}
-.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
-  float: left;
-}
-.col-xs-12 {
-  width: 100%;
-}
-.col-xs-11 {
-  width: 91.66666667%;
-}
-.col-xs-10 {
-  width: 83.33333333%;
-}
-.col-xs-9 {
-  width: 75%;
-}
-.col-xs-8 {
-  width: 66.66666667%;
-}
-.col-xs-7 {
-  width: 58.33333333%;
-}
-.col-xs-6 {
-  width: 50%;
-}
-.col-xs-5 {
-  width: 41.66666667%;
-}
-.col-xs-4 {
-  width: 33.33333333%;
-}
-.col-xs-3 {
-  width: 25%;
-}
-.col-xs-2 {
-  width: 16.66666667%;
-}
-.col-xs-1 {
-  width: 8.33333333%;
-}
-.col-xs-pull-12 {
-  right: 100%;
-}
-.col-xs-pull-11 {
-  right: 91.66666667%;
-}
-.col-xs-pull-10 {
-  right: 83.33333333%;
-}
-.col-xs-pull-9 {
-  right: 75%;
-}
-.col-xs-pull-8 {
-  right: 66.66666667%;
-}
-.col-xs-pull-7 {
-  right: 58.33333333%;
-}
-.col-xs-pull-6 {
-  right: 50%;
-}
-.col-xs-pull-5 {
-  right: 41.66666667%;
-}
-.col-xs-pull-4 {
-  right: 33.33333333%;
-}
-.col-xs-pull-3 {
-  right: 25%;
-}
-.col-xs-pull-2 {
-  right: 16.66666667%;
-}
-.col-xs-pull-1 {
-  right: 8.33333333%;
-}
-.col-xs-pull-0 {
-  right: auto;
-}
-.col-xs-push-12 {
-  left: 100%;
-}
-.col-xs-push-11 {
-  left: 91.66666667%;
-}
-.col-xs-push-10 {
-  left: 83.33333333%;
-}
-.col-xs-push-9 {
-  left: 75%;
-}
-.col-xs-push-8 {
-  left: 66.66666667%;
-}
-.col-xs-push-7 {
-  left: 58.33333333%;
-}
-.col-xs-push-6 {
-  left: 50%;
-}
-.col-xs-push-5 {
-  left: 41.66666667%;
-}
-.col-xs-push-4 {
-  left: 33.33333333%;
-}
-.col-xs-push-3 {
-  left: 25%;
-}
-.col-xs-push-2 {
-  left: 16.66666667%;
-}
-.col-xs-push-1 {
-  left: 8.33333333%;
-}
-.col-xs-push-0 {
-  left: auto;
-}
-.col-xs-offset-12 {
-  margin-left: 100%;
-}
-.col-xs-offset-11 {
-  margin-left: 91.66666667%;
-}
-.col-xs-offset-10 {
-  margin-left: 83.33333333%;
-}
-.col-xs-offset-9 {
-  margin-left: 75%;
-}
-.col-xs-offset-8 {
-  margin-left: 66.66666667%;
-}
-.col-xs-offset-7 {
-  margin-left: 58.33333333%;
-}
-.col-xs-offset-6 {
-  margin-left: 50%;
-}
-.col-xs-offset-5 {
-  margin-left: 41.66666667%;
-}
-.col-xs-offset-4 {
-  margin-left: 33.33333333%;
-}
-.col-xs-offset-3 {
-  margin-left: 25%;
-}
-.col-xs-offset-2 {
-  margin-left: 16.66666667%;
-}
-.col-xs-offset-1 {
-  margin-left: 8.33333333%;
-}
-.col-xs-offset-0 {
-  margin-left: 0%;
-}
-@media (min-width: $screen-sm) {
-  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
-    float: left;
-  }
-  .col-sm-12 {
-    width: 100%;
-  }
-  .col-sm-11 {
-    width: 91.66666667%;
-  }
-  .col-sm-10 {
-    width: 83.33333333%;
-  }
-  .col-sm-9 {
-    width: 75%;
-  }
-  .col-sm-8 {
-    width: 66.66666667%;
-  }
-  .col-sm-7 {
-    width: 58.33333333%;
-  }
-  .col-sm-6 {
-    width: 50%;
-  }
-  .col-sm-5 {
-    width: 41.66666667%;
-  }
-  .col-sm-4 {
-    width: 33.33333333%;
-  }
-  .col-sm-3 {
-    width: 25%;
-  }
-  .col-sm-2 {
-    width: 16.66666667%;
-  }
-  .col-sm-1 {
-    width: 8.33333333%;
-  }
-  .col-sm-pull-12 {
-    right: 100%;
-  }
-  .col-sm-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-sm-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-sm-pull-9 {
-    right: 75%;
-  }
-  .col-sm-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-sm-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-sm-pull-6 {
-    right: 50%;
-  }
-  .col-sm-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-sm-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-sm-pull-3 {
-    right: 25%;
-  }
-  .col-sm-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-sm-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-sm-pull-0 {
-    right: auto;
-  }
-  .col-sm-push-12 {
-    left: 100%;
-  }
-  .col-sm-push-11 {
-    left: 91.66666667%;
-  }
-  .col-sm-push-10 {
-    left: 83.33333333%;
-  }
-  .col-sm-push-9 {
-    left: 75%;
-  }
-  .col-sm-push-8 {
-    left: 66.66666667%;
-  }
-  .col-sm-push-7 {
-    left: 58.33333333%;
-  }
-  .col-sm-push-6 {
-    left: 50%;
-  }
-  .col-sm-push-5 {
-    left: 41.66666667%;
-  }
-  .col-sm-push-4 {
-    left: 33.33333333%;
-  }
-  .col-sm-push-3 {
-    left: 25%;
-  }
-  .col-sm-push-2 {
-    left: 16.66666667%;
-  }
-  .col-sm-push-1 {
-    left: 8.33333333%;
-  }
-  .col-sm-push-0 {
-    left: auto;
-  }
-  .col-sm-offset-12 {
-    margin-left: 100%;
-  }
-  .col-sm-offset-11 {
-    margin-left: 91.66666667%;
-  }
-  .col-sm-offset-10 {
-    margin-left: 83.33333333%;
-  }
-  .col-sm-offset-9 {
-    margin-left: 75%;
-  }
-  .col-sm-offset-8 {
-    margin-left: 66.66666667%;
-  }
-  .col-sm-offset-7 {
-    margin-left: 58.33333333%;
-  }
-  .col-sm-offset-6 {
-    margin-left: 50%;
-  }
-  .col-sm-offset-5 {
-    margin-left: 41.66666667%;
-  }
-  .col-sm-offset-4 {
-    margin-left: 33.33333333%;
-  }
-  .col-sm-offset-3 {
-    margin-left: 25%;
-  }
-  .col-sm-offset-2 {
-    margin-left: 16.66666667%;
-  }
-  .col-sm-offset-1 {
-    margin-left: 8.33333333%;
-  }
-  .col-sm-offset-0 {
-    margin-left: 0%;
-  }
-}
-@media (min-width: $screen-md) {
-  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
-    float: left;
-  }
-  .col-md-12 {
-    width: 100%;
-  }
-  .col-md-11 {
-    width: 91.66666667%;
-  }
-  .col-md-10 {
-    width: 83.33333333%;
-  }
-  .col-md-9 {
-    width: 75%;
-  }
-  .col-md-8 {
-    width: 66.66666667%;
-  }
-  .col-md-7 {
-    width: 58.33333333%;
-  }
-  .col-md-6 {
-    width: 50%;
-  }
-  .col-md-5 {
-    width: 41.66666667%;
-  }
-  .col-md-4 {
-    width: 33.33333333%;
-  }
-  .col-md-3 {
-    width: 25%;
-  }
-  .col-md-2 {
-    width: 16.66666667%;
-  }
-  .col-md-1 {
-    width: 8.33333333%;
-  }
-  .col-md-pull-12 {
-    right: 100%;
-  }
-  .col-md-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-md-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-md-pull-9 {
-    right: 75%;
-  }
-  .col-md-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-md-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-md-pull-6 {
-    right: 50%;
-  }
-  .col-md-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-md-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-md-pull-3 {
-    right: 25%;
-  }
-  .col-md-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-md-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-md-pull-0 {
-    right: auto;
-  }
-  .col-md-push-12 {
-    left: 100%;
-  }
-  .col-md-push-11 {
-    left: 91.66666667%;
-  }
-  .col-md-push-10 {
-    left: 83.33333333%;
-  }
-  .col-md-push-9 {
-    left: 75%;
-  }
-  .col-md-push-8 {
-    left: 66.66666667%;
-  }
-  .col-md-push-7 {
-    left: 58.33333333%;
-  }
-  .col-md-push-6 {
-    left: 50%;
-  }
-  .col-md-push-5 {
-    left: 41.66666667%;
-  }
-  .col-md-push-4 {
-    left: 33.33333333%;
-  }
-  .col-md-push-3 {
-    left: 25%;
-  }
-  .col-md-push-2 {
-    left: 16.66666667%;
-  }
-  .col-md-push-1 {
-    left: 8.33333333%;
-  }
-  .col-md-push-0 {
-    left: auto;
-  }
-  .col-md-offset-12 {
-    margin-left: 100%;
-  }
-  .col-md-offset-11 {
-    margin-left: 91.66666667%;
-  }
-  .col-md-offset-10 {
-    margin-left: 83.33333333%;
-  }
-  .col-md-offset-9 {
-    margin-left: 75%;
-  }
-  .col-md-offset-8 {
-    margin-left: 66.66666667%;
-  }
-  .col-md-offset-7 {
-    margin-left: 58.33333333%;
-  }
-  .col-md-offset-6 {
-    margin-left: 50%;
-  }
-  .col-md-offset-5 {
-    margin-left: 41.66666667%;
-  }
-  .col-md-offset-4 {
-    margin-left: 33.33333333%;
-  }
-  .col-md-offset-3 {
-    margin-left: 25%;
-  }
-  .col-md-offset-2 {
-    margin-left: 16.66666667%;
-  }
-  .col-md-offset-1 {
-    margin-left: 8.33333333%;
-  }
-  .col-md-offset-0 {
-    margin-left: 0%;
-  }
-}
-@media (min-width: $screen-lg) {
-  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
-    float: left;
-  }
-  .col-lg-12 {
-    width: 100%;
-  }
-  .col-lg-11 {
-    width: 91.66666667%;
-  }
-  .col-lg-10 {
-    width: 83.33333333%;
-  }
-  .col-lg-9 {
-    width: 75%;
-  }
-  .col-lg-8 {
-    width: 66.66666667%;
-  }
-  .col-lg-7 {
-    width: 58.33333333%;
-  }
-  .col-lg-6 {
-    width: 50%;
-  }
-  .col-lg-5 {
-    width: 41.66666667%;
-  }
-  .col-lg-4 {
-    width: 33.33333333%;
-  }
-  .col-lg-3 {
-    width: 25%;
-  }
-  .col-lg-2 {
-    width: 16.66666667%;
-  }
-  .col-lg-1 {
-    width: 8.33333333%;
-  }
-  .col-lg-pull-12 {
-    right: 100%;
-  }
-  .col-lg-pull-11 {
-    right: 91.66666667%;
-  }
-  .col-lg-pull-10 {
-    right: 83.33333333%;
-  }
-  .col-lg-pull-9 {
-    right: 75%;
-  }
-  .col-lg-pull-8 {
-    right: 66.66666667%;
-  }
-  .col-lg-pull-7 {
-    right: 58.33333333%;
-  }
-  .col-lg-pull-6 {
-    right: 50%;
-  }
-  .col-lg-pull-5 {
-    right: 41.66666667%;
-  }
-  .col-lg-pull-4 {
-    right: 33.33333333%;
-  }
-  .col-lg-pull-3 {
-    right: 25%;
-  }
-  .col-lg-pull-2 {
-    right: 16.66666667%;
-  }
-  .col-lg-pull-1 {
-    right: 8.33333333%;
-  }
-  .col-lg-pull-0 {
-    right: auto;
-  }
-  .col-lg-push-12 {
-    left: 100%;
-  }
-  .col-lg-push-11 {
-    left: 91.66666667%;
-  }
-  .col-lg-push-10 {
-    left: 83.33333333%;
-  }
-  .col-lg-push-9 {
-    left: 75%;
-  }
-  .col-lg-push-8 {
-    left: 66.66666667%;
-  }
-  .col-lg-push-7 {
-    left: 58.33333333%;
-  }
-  .col-lg-push-6 {
-    left: 50%;
-  }
-  .col-lg-push-5 {
-    left: 41.66666667%;
-  }
-  .col-lg-push-4 {
-    left: 33.33333333%;
-  }
-  .col-lg-push-3 {
-    left: 25%;
-  }
-  .col-lg-push-2 {
-    left: 16.66666667%;
-  }
-  .col-lg-push-1 {
-    left: 8.33333333%;
-  }
-  .col-lg-push-0 {
-    left: auto;
-  }
-  .col-lg-offset-12 {
-    margin-left: 100%;
-  }
-  .col-lg-offset-11 {
-    margin-left: 91.66666667%;
-  }
-  .col-lg-offset-10 {
-    margin-left: 83.33333333%;
-  }
-  .col-lg-offset-9 {
-    margin-left: 75%;
-  }
-  .col-lg-offset-8 {
-    margin-left: 66.66666667%;
-  }
-  .col-lg-offset-7 {
-    margin-left: 58.33333333%;
-  }
-  .col-lg-offset-6 {
-    margin-left: 50%;
-  }
-  .col-lg-offset-5 {
-    margin-left: 41.66666667%;
-  }
-  .col-lg-offset-4 {
-    margin-left: 33.33333333%;
-  }
-  .col-lg-offset-3 {
-    margin-left: 25%;
-  }
-  .col-lg-offset-2 {
-    margin-left: 16.66666667%;
-  }
-  .col-lg-offset-1 {
-    margin-left: 8.33333333%;
-  }
-  .col-lg-offset-0 {
-    margin-left: 0%;
-  }
-}
-.clearfix:before,
-.clearfix:after,
-.container:before,
-.container:after,
-.container-fluid:before,
-.container-fluid:after,
-.row:before,
-.row:after {
-  content: " ";
-  display: table;
-}
-.clearfix:after,
-.container:after,
-.container-fluid:after,
-.row:after {
-  clear: both;
-}
-.center-block {
-  display: block;
-  margin-left: auto;
-  margin-right: auto;
-}
index b66bab394d54ec732e12ec36b75620ffb0fe9ef8..7fd6d7ae94fa931e3ebbccecb6bbf1d34a7a25f2 100644 (file)
@@ -2,21 +2,31 @@
  * Includes the main navigation header and the faded toolbar.
  */
 
+header .grid {
+  grid-template-columns: auto min-content auto;
+}
+
+@include smaller-than($l) {
+  header .grid {
+    grid-template-columns: 1fr;
+    grid-row-gap: 0;
+  }
+}
+
 header {
+  position: relative;
   display: block;
-  z-index: 2;
+  z-index: 6;
   top: 0;
   background-color: $primary-dark;
   color: #fff;
   fill: #fff;
-  .padded {
-    padding: $-m;
-  }
   border-bottom: 1px solid #DDD;
+  box-shadow: $bs-card;
+  padding: $-xxs 0;
   .links {
     display: inline-block;
     vertical-align: top;
-    margin-left: $-m;
   }
   .links a {
     display: inline-block;
@@ -28,15 +38,6 @@ header {
     padding-left: $-m;
     padding-right: 0;
   }
-  @include smaller-than($screen-md) {
-    .links a {
-      padding-left: $-s;
-      padding-right: $-s;
-    }
-    .dropdown-container {
-      padding-left: $-s;
-    }
-  }
   .avatar, .user-name {
     display: inline-block;
   }
@@ -63,27 +64,17 @@ header {
       padding-top: 4px;
       font-size: 18px;
     }
-    @include smaller-than($screen-md) {
+    @include between($l, $xl) {
       padding-left: $-xs;
       .name {
         display: none;
       }
     }
   }
-  @include smaller-than($screen-sm) {
-    text-align: center;
-    .float.right {
-      float: none;
-    }
-    .links a {
-      padding: $-s;
-    }
-    .user-name {
-      padding-top: $-s;
-    }
-  }
 }
 
+
+
 .header-search {
   display: inline-block;
 }
@@ -92,13 +83,16 @@ header .search-box {
   margin-top: 10px;
   input {
     background-color: rgba(0, 0, 0, 0.2);
-    border: 1px solid rgba(255, 255, 255, 0.3);
+    border: 1px solid rgba(255, 255, 255, 0.2);
+    border-radius: 40px;
     color: #EEE;
     z-index: 2;
+    padding-left: 40px;
   }
   button {
     fill: #EEE;
     z-index: 1;
+    left: 16px;
     svg {
       margin-right: 0;
     }
@@ -115,20 +109,11 @@ header .search-box {
   :-moz-placeholder { /* Firefox 18- */
     color: #DDD;
   }
-  @include smaller-than($screen-lg) {
-    max-width: 250px;
-  }
-  @include smaller-than($l) {
+  @include between($l, $xl) {
     max-width: 200px;
   }
 }
 
-@include smaller-than($s) {
-  .header-search {
-    display: block;
-  }
-}
-
 .logo {
   display: inline-block;
   &:hover {
@@ -151,10 +136,184 @@ header .search-box {
   height: 43px;
 }
 
-.breadcrumbs span.sep {
-  color: #aaa;
+.mobile-menu-toggle {
+  color: #FFF;
+  fill: #FFF;
+  font-size: 2em;
+  border: 2px solid rgba(255, 255, 255, 0.8);
+  border-radius: 4px;
   padding: 0 $-xs;
+  position: absolute;
+  right: $-m;
+  top: 13px;
+  line-height: 1;
+  cursor: pointer;
+  user-select: none;
+  svg {
+    margin: 0;
+    bottom: -2px;
+  }
 }
+
+@include smaller-than($l) {
+  header .header-links {
+    display: none;
+    background-color: #FFF;
+    z-index: 10;
+    right: $-m;
+    border-radius: 4px;
+    overflow: hidden;
+    position: absolute;
+    box-shadow: $bs-hover;
+    margin-top: -$-xs;
+    &.show {
+      display: block;
+    }
+  }
+  header .links a, header .dropdown-container ul li a {
+    text-align: left;
+    display: block;
+    padding: $-s $-m;
+    color: $text-dark;
+    fill: $text-dark;
+    svg {
+      margin-right: $-s;
+    }
+    &:hover {
+      background-color: #EEE;
+      color: #444;
+      fill: #444;
+      text-decoration: none;
+    }
+  }
+  header .dropdown-container {
+    display: block;
+    padding-left: 0;
+  }
+  header .links {
+    display: block;
+  }
+  header .dropdown-container ul {
+    display: block !important;
+    position: relative;
+    background-color: transparent;
+    border: 0;
+    padding: 0;
+    margin: 0;
+    box-shadow: none;
+  }
+}
+
+.tri-layout-mobile-tabs {
+  position: sticky;
+  top: 0;
+  z-index: 5;
+  background-color: #FFF;
+  border-bottom: 1px solid #DDD;
+  box-shadow: $bs-card;
+}
+.tri-layout-mobile-tab {
+  text-align: center;
+  border-bottom: 3px solid #BBB;
+  &:first-child {
+    border-right: 1px solid #DDD;
+  }
+  &.active {
+    border-bottom-color: currentColor;
+  }
+}
+
+.breadcrumbs {
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  justify-content: flex-start;
+  flex-wrap: wrap;
+  opacity: 0.7;
+  .icon-list-item {
+    width: auto;
+    padding-top: $-xs;
+    padding-bottom: $-xs;
+  }
+  .separator {
+    display: inline-block;
+    fill: #aaa;
+    font-size: 1.6em;
+    line-height: 0.8;
+    margin: -2px 0 0;
+  }
+  &:hover {
+    opacity: 1;
+  }
+}
+
+@include smaller-than($l) {
+  .breadcrumbs .icon-list-item {
+    padding: $-xs;
+    > span + span {
+      display: none;
+    }
+    > span:first-child {
+      margin-right: 0;
+    }
+  }
+}
+
+.breadcrumb-listing {
+  position: relative;
+  .breadcrumb-listing-toggle {
+    padding: 6px;
+    border: 1px solid transparent;
+    border-radius: 4px;
+    &:hover {
+      border-color: #DDD;
+    }
+  }
+  .svg-icon {
+    margin-right: 0;
+  }
+}
+
+.breadcrumb-listing-dropdown {
+  box-shadow: $bs-med;
+  overflow: hidden;
+  min-height: 100px;
+  width: 240px;
+  display: none;
+  position: absolute;
+  z-index: 80;
+  right: -$-m;
+  .breadcrumb-listing-search .svg-icon {
+    position: absolute;
+    left: $-s;
+    top: 11px;
+    fill: #888;
+    pointer-events: none;
+  }
+  .breadcrumb-listing-entity-list {
+    max-height: 400px;
+    overflow-y: scroll;
+    text-align: left;
+  }
+  input {
+    padding-left: $-xl;
+    border-radius: 0;
+    border: 0;
+    border-bottom: 1px solid #DDD;
+  }
+}
+
+@include smaller-than($m) {
+  .breadcrumb-listing-dropdown {
+    position: fixed;
+    right: auto;
+    left: $-m;
+  }
+  .breadcrumb-listing-dropdown .breadcrumb-listing-entity-list {
+    max-height: 240px;
+  }
+}
+
 .faded {
   a, button, span, span > div {
     color: #666;
@@ -175,20 +334,9 @@ header .search-box {
   padding: $-s;
 }
 
-.faded-small {
-  color: #000;
-  fill: #000;
-  font-size: 0.9em;
-  background-color: $primary-faded;
-}
-
-.toolbar-container {
-  background-color: #FFF;
-}
-
-.breadcrumbs .text-button, .action-buttons .text-button {
+.action-buttons .text-button {
   display: inline-block;
-  padding: $-s;
+  padding: $-xs $-s;
   &:last-child {
     padding-right: 0;
   }
@@ -217,28 +365,12 @@ header .search-box {
 }
 
 @include smaller-than($m) {
-  .breadcrumbs .text-button, .action-buttons .text-button {
+  .action-buttons .text-button {
     padding: $-xs $-xs;
   }
   .action-buttons .dropdown-container:last-child a {
     padding-left: $-xs;
   }
-  .breadcrumbs .text-button {
-    font-size: 0;
-  }
-  .breadcrumbs .text-button svg {
-    font-size: $fs-m;
-  }
-  .breadcrumbs a i {
-    font-size: $fs-m;
-    padding-right: 0;
-  }
-  .breadcrumbs span.sep {
-    padding: 0 $-xxs;
-  }
-  .toolbar .col-xs-1:first-child {
-    padding-right: 0;
-  }
 }
 
 .nav-tabs {
@@ -253,7 +385,4 @@ header .search-box {
       border-bottom: 2px solid $primary;
     }
   }
-}
-.faded-small .nav-tabs a {
-  padding: $-s $-m;
 }
\ No newline at end of file
index 65f05a71d5c69370707cca7b45b4510395ff8b05..7c3a3c49b41179aaf673b211ed834a399ca8a54e 100644 (file)
@@ -3,27 +3,18 @@
 }
 
 html {
-  background-color: #FFFFFF;
   height: 100%;
   overflow-y: scroll;
+  background-color: #F2F2F2;
   &.flexbox {
     overflow-y: hidden;
   }
-  &.shaded {
-    background-color: #F2F2F2;
-  }
 }
 
 body {
   font-size: $fs-m;
   line-height: 1.6;
-  color: #616161;
+  color: #444;
   -webkit-font-smoothing: antialiased;
-  &.shaded {
-    background-color: #F2F2F2;
-  }
-}
-
-button {
-  font-size: 100%;
+  background-color: #F2F2F2;
 }
\ No newline at end of file
diff --git a/resources/assets/sass/_layout.scss b/resources/assets/sass/_layout.scss
new file mode 100644 (file)
index 0000000..1370489
--- /dev/null
@@ -0,0 +1,312 @@
+
+/**
+ * Generic content container
+ */
+.container {
+  max-width: $xxl;
+  margin-left: auto;
+  margin-right: auto;
+  padding-left: $-m;
+  padding-right: $-m;
+  &.small {
+    max-width: 840px;
+  }
+  &.very-small {
+    max-width: 480px;
+  }
+}
+
+/**
+ * Core grid layout system
+ */
+.grid {
+  display: grid;
+  grid-column-gap: $-l;
+  grid-row-gap: $-l;
+  &.half {
+    grid-template-columns: 1fr 1fr;
+  }
+  &.third {
+    grid-template-columns: 1fr 1fr 1fr;
+  }
+  &.left-focus {
+    grid-template-columns: 2fr 1fr;
+  }
+  &.right-focus {
+    grid-template-columns: 1fr 3fr;
+  }
+  &.gap-y-xs {
+    grid-row-gap: $-xs;
+  }
+  &.gap-xl {
+    grid-column-gap: $-xl;
+    grid-row-gap: $-xl;
+  }
+  &.gap-xxl {
+    grid-column-gap: $-xxl;
+    grid-row-gap: $-xxl;
+  }
+  &.v-center {
+    align-items: center;
+  }
+  &.no-gap {
+    grid-row-gap: 0;
+    grid-column-gap: 0;
+  }
+  &.no-row-gap {
+    grid-row-gap: 0;
+  }
+}
+
+@include smaller-than($m) {
+  .grid.third {
+    grid-template-columns: 1fr 1fr;
+  }
+  .grid.half:not(.no-break), .grid.left-focus:not(.no-break), .grid.right-focus:not(.no-break) {
+    grid-template-columns: 1fr;
+  }
+  .grid.half.collapse-xs {
+    grid-template-columns: 1fr 1fr;
+  }
+  .grid.gap-xl {
+    grid-column-gap: $-m;
+    grid-row-gap: $-m;
+  }
+  .grid.right-focus.reverse-collapse > *:nth-child(2) {
+    order: 0;
+  }
+  .grid.right-focus.reverse-collapse > *:nth-child(1) {
+    order: 1;
+  }
+}
+
+@include smaller-than($s) {
+  .grid.third {
+    grid-template-columns: 1fr;
+  }
+}
+
+@include smaller-than($xs) {
+  .grid.half.collapse-xs {
+    grid-template-columns: 1fr;
+  }
+}
+
+/**
+ * Flexbox layout system
+ */
+body.flexbox {
+  display: flex;
+  flex-direction: column;
+  align-items: stretch;
+  height: 100%;
+  min-height: 100%;
+  max-height: 100%;
+  overflow: hidden;
+  #content {
+    flex: 1;
+    display: flex;
+    min-height: 0;
+  }
+}
+
+.flex-fill {
+  display: flex;
+  align-items: stretch;
+  min-height: 0;
+  max-width: 100%;
+  position: relative;
+}
+
+.flex {
+  min-height: 0;
+  flex: 1;
+}
+
+
+/**
+ * Display and float utilities
+ */
+.block {
+  display: block;
+  position: relative;
+}
+
+.inline {
+  display: inline;
+}
+
+.block.inline {
+  display: inline-block;
+}
+
+.float {
+  float: left;
+  &.right {
+    float: right;
+  }
+}
+
+/**
+ * Visibility
+ */
+@each $sizeLetter, $size in $screen-sizes {
+  @include smaller-than($size) {
+    .hide-under-#{$sizeLetter} {
+      display: none !important;
+    }
+  }
+  @include larger-than($size) {
+    .hide-over-#{$sizeLetter} {
+      display: none !important;
+    }
+  }
+}
+
+/**
+ * Inline content columns
+ */
+.dual-column-content {
+  columns: 2;
+}
+
+@include smaller-than($m) {
+  .dual-column-content {
+    columns: 1;
+  }
+}
+
+
+/**
+ * Fixes
+ */
+.clearfix:before,
+.clearfix:after {
+  content: " ";
+  display: table;
+}
+.clearfix:after {
+  clear: both;
+}
+
+/**
+ * View Layouts
+ */
+.tri-layout-container {
+  display: grid;
+  margin-left: $-xl;
+  margin-right: $-xl;
+  grid-template-columns: 1fr 4fr 1fr;
+  grid-template-areas: "a b c";
+  grid-column-gap: $-xxl;
+  .tri-layout-right {
+    grid-area: c;
+    min-width: 0;
+  }
+  .tri-layout-left {
+    grid-area: a;
+    min-width: 0;
+  }
+  .tri-layout-middle {
+    grid-area: b;
+    padding-top: $-m;
+  }
+}
+@include smaller-than($xxl) {
+  .tri-layout-container {
+    grid-template-areas:  "c b b"
+    "a b b";
+    grid-template-columns: 1fr 3fr;
+    grid-template-rows: max-content min-content;
+    padding-right: $-l;
+  }
+}
+@include larger-than($xxl) {
+  .tri-layout-left-contents, .tri-layout-right-contents {
+    padding: $-m;
+    position: sticky;
+    top: $-m;
+    max-height: 100vh;
+    min-height: 50vh;
+    overflow-y: scroll;
+    overflow-x: hidden;
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+    &::-webkit-scrollbar {
+      display: none;
+    }
+  }
+  .tri-layout-middle-contents {
+    max-width: 940px;
+    margin: 0 auto;
+  }
+}
+
+@include smaller-than($l) {
+  .tri-layout-container {
+    grid-template-areas:  none;
+    grid-template-columns: 1fr;
+    grid-column-gap: 0;
+    padding-right: $-xs;
+    padding-left: $-xs;
+    .tri-layout-left-contents, .tri-layout-right-contents {
+      padding-left: $-m;
+      padding-right: $-m;
+    }
+    .tri-layout-right-contents > div, .tri-layout-left-contents > div {
+      opacity: 0.6;
+      z-index: 0;
+    }
+    .tri-layout-left > *, .tri-layout-right > * {
+      display: none;
+      pointer-events: none;
+    }
+    .tri-layout-left, .tri-layout-right {
+      grid-area: none;
+      grid-column: 1/1;
+      grid-row: 1;
+      padding-top: 0 !important;
+    }
+    .tri-layout-middle {
+      grid-area: none;
+      grid-row: 3;
+      grid-column: 1/1;
+      z-index: 1;
+      overflow: hidden;
+      transition: transform ease-in-out 240ms;
+    }
+    .tri-layout-left {
+      grid-row: 2;
+    }
+    &.show-info {
+      overflow: hidden;
+      .tri-layout-middle {
+        display: none;
+      }
+      .tri-layout-right  > *, .tri-layout-left > * {
+        display: block;
+        pointer-events: auto;
+      }
+    }
+  }
+}
+@include larger-than($l) {
+  .tri-layout-mobile-tabs {
+    display: none;
+  }
+}
+
+@include smaller-than($m) {
+  .tri-layout-container {
+    margin-left: 0;
+    margin-right: 0;
+  }
+}
+
+.tri-layout-left-contents > div, .tri-layout-right-contents > div {
+  opacity: 0.6;
+  transition: opacity ease-in-out 120ms;
+  &:hover {
+    opacity: 1;
+  }
+}
\ No newline at end of file
index 18a7ea9cee0d52b617c8b5784d36f8abbf35b846..dc4dc8816cd0a4076be6fcb1c60403165b8fb9f3 100644 (file)
-.page-list {
-  h4 {
-    margin: $-l 0 $-xs 0;
-    font-size: 1.666em;
+
+.book-contents .entity-list-item {
+  .icon {
+    width: 4px;
+    border-radius: 1px;
+    justify-self: stretch;
+    align-self: stretch;
+    height: auto;
+    margin-right: $-l;
   }
-  a.chapter {
-    color: $color-chapter;
+  .icon:after {
+    opacity: 0.5;
   }
-  .inset-list {
+  .icon svg {
     display: none;
-    overflow: hidden;
   }
-  h5 {
-    display: block;
-    margin: $-s 0 0 0;
-    border-left: 5px solid $color-page;
-    padding: $-xs 0 $-xs $-m;
-    font-size: 1.1em;
-    font-weight: normal;
-    &.draft {
-      border-left-color: $color-page-draft;
-    }
-  }
-  .entity-list-item {
-    margin-bottom: $-m;
-  }
-  hr {
-    margin-top: 0;
-  }
-  .page, .chapter, .book {
-    padding-left: $-l;
+  p {
+    margin-bottom: 0;
   }
-  .page {
-    border-left: 5px solid $color-page;
+  .inner-page {
+    padding-top: 0;
+    padding-bottom: 0;
   }
-  .page.draft {
-    border-left: 5px solid $color-page-draft;
-    .text-page {
-      color: $color-page-draft;
-      fill: $color-page-draft;
+}
+
+.entity-list-item + .chapter-expansion {
+  display: flex;
+  padding: 0 $-m $-m $-m;
+  align-items: center;
+  border: 0;
+  width: 100%;
+  position: relative;
+  > .icon {
+    width: 4px;
+    height: auto;
+    border-radius: 0 0 1px 1px;
+    align-self: stretch;
+    flex-shrink: 0;
+    &:before {
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 100%;
+      height: 1px;
+      background-color: currentColor;
+      content: '';
+      opacity: 0.5;
+    }
+    &:after {
+      opacity: 0.5;
     }
   }
-  .chapter {
-    border-left: 5px solid $color-chapter;
+  .icon svg {
+    display: none;
   }
-  .book {
-    border-left: 5px solid $color-book;
+  > .content {
+    flex: 1;
   }
-  .meta {
-    margin-top: -$-m;
-    font-size: 0.95em;
+  .chapter-expansion-toggle {
+    border-radius: 0 4px 4px 0;
+    padding: $-xs $-m;
   }
-  .meta span {
-    margin-right: $-s;
+  .chapter-expansion-toggle:hover {
+    background-color: rgba(0, 0, 0, 0.06);
   }
 }
 
-@include smaller-than($s) {
-  .page-list h4 {
-    font-size: 1.333em;
+.entity-list-item.has-children {
+  padding-bottom: 0;
+  > .icon {
+    border-radius: 4px 4px 0 0;
+  }
+}
+
+.inset-list {
+  display: none;
+  .entity-list-item-name {
+    font-size: 1rem;
+  }
+  .entity-list-item-children {
+    padding-top: 0;
+    padding-bottom: 0;
   }
 }
 
 .sidebar-page-nav {
-  $nav-indent: $-s;
+  $nav-indent: $-m;
   list-style: none;
-  margin: $-s 0 $-m 2px;
-  border-left: 2px dotted #BBB;
+  margin: $-s 0 $-m $-xs;
+  position: relative;
+  &:after {
+    content: '';
+    display: block;
+    position: absolute;
+    left: 0;
+    background-color: rgba(0, 0, 0, 0.2);
+    width: 2px;
+    top: 5px;
+    bottom: 5px;
+    z-index: 0;
+  }
   li {
-    padding-left: $-s;
     margin-bottom: 4px;
     font-size: 0.95em;
+    position: relative;
   }
   .h1 {
-    margin-left: -2px;
+    padding-left: $nav-indent;
   }
   .h2 {
-    margin-left: -2px;
+    padding-left: $nav-indent;
   }
   .h3 {
-    margin-left: $nav-indent;
+    padding-left: $nav-indent * 2;
   }
   .h4 {
-    margin-left: $nav-indent*2;
+    padding-left: $nav-indent * 2.5;
   }
   .h5 {
-    margin-left: $nav-indent*3;
+    padding-left: $nav-indent*3;
   }
   .h6 {
-    margin-left: $nav-indent*4;
+    padding-left: $nav-indent*3.5;
   }
   .current-heading {
     font-weight: bold;
   }
+  li:not(.current-heading) .sidebar-page-nav-bullet {
+    background-color: #BBB !important;
+  }
+  .sidebar-page-nav-bullet {
+    width: 6px;
+    height: 6px;
+    position: absolute;
+    left: -2px;
+    top: 30%;
+    border-radius: 50%;
+    box-shadow: 0 0 0 6px #F2F2F2;
+    z-index: 1;
+  }
 }
 
 // Sidebar list
-.book-tree {
-  transition: ease-in-out 240ms;
-  transition-property: right, border;
-}
-.book-tree h4 {
-  padding: $-m $-s 0 $-s;
-  i {
-    padding-right: $-s;
-  }
-}
-.book-tree .sidebar-page-list {
+.book-tree .sidebar-page-list  {
   list-style: none;
-  margin: $-xs 0 0;
+  margin: $-xs -$-s 0 -$-s;
   padding-left: 0;
-  border-left: 5px solid $color-book;
-  li a {
-    display: block;
-    border-bottom: none;
-    padding: $-xs 0 $-xs $-s;
-    &:hover {
-      text-decoration: none;
-    }
-  }
-  li a i {
-    padding-right: $-xs + 2px;
-  }
-  li, a {
+  padding-right: 0;
+  position: relative;
+
+  &:after, .sub-menu:after {
+    content: '';
     display: block;
+    position: absolute;
+    left: $-m;
+    top: 1rem;
+    bottom: 1rem;
+    border-left: 4px solid rgba(0, 0, 0, 0.1);
+    z-index: 0;
   }
-  a.bold {
-    color: #EEE !important;
-    fill: #EEE !important;
-  }
+
   ul {
     list-style: none;
-    margin: 0;
+    padding-left: 1rem;
+    padding-right: 0;
   }
-  .book {
-    color: $color-book !important;
-    fill: $color-book !important;
-    &.selected {
-      background-color: rgba($color-book, 0.29);
+  .entity-list-item {
+    padding-top: $-xxs;
+    padding-bottom: $-xxs;
+    .content {
+      padding-top: $-xs;
+      padding-bottom: $-xs;
+      max-width: calc(100% - 20px);
     }
   }
-  .chapter {
-    color: $color-chapter !important;
-    fill: $color-chapter !important;
-    &.selected {
-      background-color: rgba($color-chapter, 0.12);
-    }
+  .entity-list-item.no-hover {
+    margin-top: -$-xs;
+    padding-right: 0;
   }
-  .page {
-    color: $color-page !important;
-    fill: $color-page !important;
-    border-bottom: none;
-    &.selected {
-      background-color: rgba($color-page, 0.1);
-    }
+  .entity-list-item-name {
+    font-size: 1em;
+    margin: 0;
   }
-  [chapter-toggle] {
-    padding-left: $-s;
+  .chapter-child-menu {
+    font-size: .8rem;
+    margin-top: -.2rem;
+    margin-left: -1rem;
   }
-  .list-item-chapter {
-    border-left: 5px solid $color-chapter;
-    margin: 10px 10px;
-    display: block;
+  [chapter-toggle] {
+    padding-left: .7rem;
+    padding-bottom: .2rem;
   }
-  .list-item-page {
-    border-bottom: none;
-    border-left: 5px solid $color-page;
-    margin: 10px 10px;
+  .entity-list-item .icon {
+    z-index: 2;
+    width: 4px;
+    height: auto;
+    align-self: stretch;
+    flex-shrink: 0;
+    border-radius: 1px;
+    opacity: 0.6;
   }
-  .list-item-page.draft {
-    border-left: 5px solid $color-page-draft;
+  .entity-list-item .icon:after {
+    opacity: 1;
   }
-  .page.draft .page, .list-item-page.draft a.page {
-    color: $color-page-draft !important;
-    fill: $color-page-draft !important;
+  .entity-list-item .icon svg {
+    display: none;
   }
-  .sub-menu {
+}
+
+.chapter-child-menu {
+  ul.sub-menu {
     display: none;
     padding-left: 0;
+    position: relative;
   }
   [chapter-toggle].open + .sub-menu {
     display: block;
 // Sortable Lists
 .sortable-page-list, .sortable-page-list ul {
   list-style: none;
-  background-color: #FFF;
 }
 .sort-box {
   margin-bottom: $-m;
-  padding: 0 $-l 0 $-l;
-  border-left: 4px solid $color-book;
+  border: 2px solid rgba($color-book, 0.6);
+  padding: $-m $-xl;
+  border-radius: 4px;
+}
+.sort-box-options {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+}
+.sort-box-options .button {
+  margin-left: 0;
 }
 .sortable-page-list {
   margin-left: 0;
+  padding: 0;
+  .entity-list-item > span:first-child {
+    align-self: flex-start;
+  }
+  .entity-list-item > div {
+    display: block;
+    flex: 1;
+  }
   > ul {
     margin-left: 0;
   }
   ul {
-    margin-bottom: 0;
+    margin-bottom: $-m;
     margin-top: 0;
-    box-shadow: 0 0 1px 0 rgba(0, 0, 0, 0.1);
+    padding-left: $-m;
   }
   li {
     border: 1px solid #DDD;
-    padding: $-xs $-s;
     margin-top: -1px;
     min-height: 38px;
     &.text-chapter {
 
 .activity-list-item {
   padding: $-s 0;
+  display: grid;
+  grid-template-columns: min-content 1fr;
+  grid-column-gap: $-m;
   color: #888;
   fill: #888;
-  border-bottom: 1px solid #EEE;
   font-size: 0.9em;
-  .left {
-    float: left;
-  }
-  .left + .right {
-    margin-left: 30px + $-s;
-  }
-  &:last-of-type {
-    border-bottom: 0;
+}
+.card .activity-list-item {
+  padding: $-s $-m;
+}
+
+.user-list-item {
+  display: inline-grid;
+  padding: $-s;
+  grid-template-columns: min-content 1fr;
+  grid-column-gap: $-m;
+  font-size: 0.9em;
+  align-items: center;
+  > div:first-child {
+    line-height: 0;
   }
 }
 
@@ -280,10 +338,8 @@ ul.pagination {
   margin: 0;
 }
 
-.entity-list {
-  > div {
-    padding: $-m 0;
-  }
+.entity-list, .icon-list {
+  margin: 0 (-$-m);
   h4 {
     margin: 0;
   }
@@ -302,15 +358,125 @@ ul.pagination {
     color: $color-page-draft;
     fill: $color-page-draft;
   }
+  > .dropdown-container {
+    display: block;
+  }
+}
+
+.icon-list hr {
+  margin: $-s $-m;
+  max-width: 140px;
+  opacity: 0.25;
+  height: 1.1px;
+}
+
+.icon-list hr + hr, .icon-list hr:first-child, .icon-list hr:last-child {
+  display: none;
+}
+
+.entity-list-item, .icon-list-item {
+  padding: $-s $-m;
+  display: flex;
+  align-items: center;
+  background-color: transparent;
+  border: 0;
+  width: 100%;
+  position: relative;
+  word-break: break-word;
+  h4 a {
+    color: #666;
+  }
+  > span:first-child {
+    margin-right: $-m;
+    flex-basis: 1.88em;
+    flex: none;
+  }
+  > span:last-child {
+    flex: 1;
+    text-align: left;
+  }
+  &:not(.no-hover) {
+    cursor: pointer;
+  }
+  &:not(.no-hover):hover {
+    text-decoration: none;
+    background-color: rgba(0, 0, 0, 0.1);
+    border-radius: 4px;
+  }
+  &.outline-hover {
+    border: 1px solid transparent;
+  }
+  &.outline-hover:hover {
+    background-color: transparent;
+    border-color: rgba(0, 0, 0, 0.1);
+  }
 }
 
-.card .entity-list-item, .card .activity-list-item {
-  padding-left: $-m;
-  padding-right: $-m;
+.entity-list-item-path-sep {
+  display: inline-block;
+  vertical-align: top;
+  position: relative;
+  top: 1px;
+  svg {
+    margin-right: 0;
+  }
+}
+
+.card .entity-list-item:not(.no-hover):hover {
+  background-color: #F2F2F2;
+}
+.card .entity-list-item .entity-list-item:hover {
+  background-color: #EEEEEE;
+}
+
+.entity-list-item-children {
+  padding: $-m;
+  > div {
+    overflow: hidden;
+    padding: $-xs 0;
+    margin-top: -$-xs;
+  }
+  .entity-chip {
+    text-overflow: ellipsis;
+    height: 2.5em;
+    overflow: hidden;
+    text-align: left;
+    display: block;
+    white-space: nowrap;
+  }
+}
+
+.entity-list-item-image {
+  align-self: stretch;
+  width: 140px;
+  flex: none;
+  background-size: cover;
+  background-position: 50% 50%;
+  border-radius: 3px;
+  position: relative;
+  margin-right: $-l;
+
+  .svg-icon {
+    color: #FFF;
+    fill: #FFF;
+    font-size: 1.66rem;
+    margin-right: 0;
+    position: absolute;
+    bottom: $-xs;
+    left: $-xs;
+  }
+
+  @include smaller-than($m) {
+    width: 80px;
+  }
+}
+
+.chapter > .entity-list-item-image {
+  width: 60px;
 }
 
 .entity-list.compact {
-  font-size: 0.6em;
+  font-size: 0.6 * $fs-m;
   h4, a {
     line-height: 1.2;
   }
@@ -331,6 +497,11 @@ ul.pagination {
   hr {
     margin: 0;
   }
+  @include smaller-than($m) {
+    h4 {
+      font-size: 1.666em;
+    }
+  }
 }
 
 .dropdown-container {
@@ -363,9 +534,8 @@ ul.pagination {
     color: #999;
     fill: #999;
   }
-  li.padded {
-    padding: $-xs $-m;
-    line-height: 1.2;
+  li.active a {
+    font-weight: 600;
   }
   a, button {
     display: block;
@@ -396,7 +566,10 @@ ul.pagination {
 .featured-image-container {
   position: relative;
   overflow: hidden;
-  background: #F2F2F2;
+  min-height: 140px;
+  background-size: cover;
+  background-position: 50% 50%;
+  transition: opacity ease-in-out 240ms;
   a {
     display: block;
   }
@@ -405,11 +578,46 @@ ul.pagination {
     width: 100%;
     max-width: 100%;
     height: auto;
-    transition: all .5s ease-in-out;
   }
-  img:hover {
-    transform: scale(1.15);
-    opacity: .5;
+}
+.featured-image-container-wrap {
+  position: relative;
+  .svg-icon {
+    color: #FFF;
+    fill: #FFF;
+    font-size: 2rem;
+    margin-right: 0;
+    position: absolute;
+    bottom: 10px;
+    left: 6px;
   }
 }
+.grid-card:hover .featured-image-container {
+  opacity: .5;
+}
+
+.action-link-list {
+  //padding: $-s 0;
+}
+.action-link {
+  background: transparent;
+  border: none;
+  color: currentColor;
+  padding: $-m 0;
+}
 
+.active-link-list {
+  a {
+    display: inline-block;
+    padding: $-s;
+  }
+  a:not(.active) {
+    color: #444;
+    fill: #444;
+  }
+  a:hover {
+    background-color: rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    text-decoration: none;
+  }
+}
\ No newline at end of file
index 3d3101ca7130d152d1237976892c08a0d1b2c85b..1c45ebd3080b92ff4f14ebce0f181e84dac71100 100644 (file)
@@ -5,12 +5,6 @@
 @mixin larger-than($size) {
     @media screen and (min-width: $size) { @content; }
 }
-@mixin clearfix() {
-  &:after {
-    display: block;
-    content: '';
-    font-size: 0;
-    clear: both;
-    position: relative;
-  }
+@mixin between($min, $max) {
+  @media screen and (min-width: $min) and (max-width: $max) { @content; }
 }
index 84280319debff1d04fd30d4633ca0448b66d6399..969682c3ba8c5947eb34977932f89353e1b7daaa 100755 (executable)
@@ -3,12 +3,12 @@
   flex-direction: column;
   align-items: stretch;
   overflow: hidden;
-  .faded-small {
-    height: auto;
-  }
+  background-color: #FFF;
+
   .edit-area {
     flex: 1;
     flex-direction: column;
+    z-index: 1;
   }
 
   .mce-tinymce {
   }
 }
 
+@include smaller-than($l) {
+  .page-edit-toolbar {
+    overflow-x: scroll;
+    overflow-y: visible;
+    z-index: 4;
+  }
+  .page-edit-toolbar .grid.third {
+    display: block;
+    white-space: nowrap;
+    > div {
+      display: inline-block;
+    }
+  }
+}
+
+@include smaller-than($l) {
+  .page-edit-toolbar #save-button {
+    position: fixed;
+    z-index: 30;
+    background-color: #FFF;
+    border-radius: 50%;
+    width: 42px;
+    height: 42px;
+    font-size: 16px;
+    right: $-m;
+    bottom: $-xs;
+    box-shadow: $bs-med;
+    span {
+      display: none;
+    }
+  }
+}
+
 .draft-notification {
   pointer-events: none;
   transform: scale(0);
   width: 100%;
   max-width: 840px;
   margin: 0 auto;
-  margin-top: $-xxl;
   overflow-wrap: break-word;
-  &.flex {
-    margin-top: $-m;
-  }
   .align-left {
     text-align: left;
   }
     min-height: 0px;
     overflow-y: scroll;
   }
-  div[toolbox-tab-content] .padded {
-    flex: 1;
-    padding-top: 0;
-  }
-  div[toolbox-tab-content] .padded.files {
-       overflow-x: hidden;
-  }
   h4 {
     font-size: 24px;
     margin: $-m 0 0 0;
   }
 }
 
-.comments-container {
-  width: 100%;
-  border-top: 1px solid #DDD;
-  margin-top: $-xl;
-  margin-bottom: $-m;
-  h5 {
-    color: #888;
-    font-weight: normal;
-    margin-top: 0.5em;
-  }
+.comments-container h5 {
+  color: #888;
+  font-weight: normal;
+  margin-top: 0.5em;
 }
 
 .comment-editor .CodeMirror, .comment-editor .CodeMirror-scroll {
   .mce-open {
     display: none;
   }
+}
+
+.entity-list-item > span:first-child, .icon-list-item > span:first-child, .chapter-expansion > .icon {
+  font-size: 0.8rem;
+  width: 1.88em;
+  height: 1.88em;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  border-radius: 1em;
+  position: relative;
+  overflow: hidden;
+  svg {
+    margin: 0;
+    bottom: 0;
+  }
+  &:after {
+    content: '';
+    position: absolute;
+    background-color: currentColor;
+    opacity: 0.2;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
+}
+
+.entity-chip {
+  display: inline-block;
+  align-items: center;
+  justify-content: center;
+  text-align: center;
+  font-size: 0.9em;
+  border-radius: 3px;
+  position: relative;
+  overflow: hidden;
+  padding: $-xs $-s;
+  fill: currentColor;
+  opacity: 0.85;
+  transition: opacity ease-in-out 120ms;
+  &:after {
+    content: '';
+    position: absolute;
+    background-color: currentColor;
+    opacity: 0.15;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+  }
+  &:hover {
+    text-decoration: none;
+    opacity: 1;
+  }
 }
\ No newline at end of file
diff --git a/resources/assets/sass/_spacing.scss b/resources/assets/sass/_spacing.scss
new file mode 100644 (file)
index 0000000..69ed5a2
--- /dev/null
@@ -0,0 +1,32 @@
+// Here we generate spacing utility classes for our sizes for all box sides and axis.
+// These will output to classes like .px-m (Padding on x-axis, medium size) or .mr-l (Margin right, large size)
+
+@mixin spacing($prop, $propLetter) {
+  @each $sizeLetter, $size in $spacing {
+    .#{$propLetter}-#{$sizeLetter} {
+      #{$prop}: $size !important;
+    }
+    .#{$propLetter}x-#{$sizeLetter} {
+      #{$prop}-left: $size !important;
+      #{$prop}-right: $size !important;
+    }
+    .#{$propLetter}y-#{$sizeLetter} {
+      #{$prop}-top: $size !important;
+      #{$prop}-bottom: $size !important;
+    }
+    .#{$propLetter}t-#{$sizeLetter} {
+      #{$prop}-top: $size !important;
+    }
+    .#{$propLetter}r-#{$sizeLetter} {
+      #{$prop}-right: $size !important;
+    }
+    .#{$propLetter}b-#{$sizeLetter} {
+      #{$prop}-bottom: $size !important;
+    }
+    .#{$propLetter}l-#{$sizeLetter} {
+      #{$prop}-left: $size !important;
+    }
+  }
+}
+@include spacing('margin', 'm')
+@include spacing('padding', 'p')
\ No newline at end of file
index ec24e2fa64f51be5be1cda6eab84eca656651244..a1a2fef0a44537057b186fdd1969bf7461edb175 100644 (file)
@@ -19,13 +19,13 @@ table {
 
 table.table {
   width: 100%;
-  tr {
-    border-bottom: 1px solid #DDD;
+  tr td, tr th {
+    border-bottom: 1px solid rgba(0, 0, 0, 0.05);
   }
   th, td {
     text-align: left;
     border: none;
-    padding: $-xs $-xs;
+    padding: $-s $-s;
     vertical-align: middle;
     margin: 0;
   }
@@ -44,6 +44,9 @@ table.table {
   td.actions {
     overflow: visible;
   }
+  a {
+    display: inline-block;
+  }
 }
 
 table.no-style {
index 0063c4672cca2c9d257c28e57c449b483c74f499..41c99bbe594eabde42ca80f97db763a201817cc8 100644 (file)
@@ -42,7 +42,7 @@ h1, h2, h3, h4, h5, h6 {
   font-weight: 400;
   position: relative;
   display: block;
-  color: #555;
+  color: #222;
   .subheader {
     font-size: 0.5em;
     line-height: 1em;
@@ -55,7 +55,6 @@ h5 {
 }
 
 h5, h6 {
-  font-weight: 500;
   line-height: 1.2em;
   margin-top: 0.78571429em;
   margin-bottom: 0.66em;
@@ -79,10 +78,18 @@ h5, h6 {
   }
 }
 
+.list-heading {
+  font-size: 2rem;
+}
+
+h2.list-heading {
+  font-size: 1.333rem;
+}
+
 /*
  * Link styling
  */
-a, .link {
+a {
   color: $primary;
   cursor: pointer;
   text-decoration: none;
@@ -141,11 +148,8 @@ em, i, .italic {
 }
 
 small, p.small, span.small, .text-small {
-  font-size: 0.8em;
-  color: lighten($text-dark, 20%);
-  small, p.small, span.small, .text-small {
-    font-size: 1em;
-  }
+  font-size: 0.75rem;
+  color: lighten($text-dark, 10%);
 }
 
 sup, .superscript {
@@ -233,106 +237,6 @@ pre code {
   display: block;
   line-height: 1.6;
 }
-/*
- * Text colors
- */
-p.pos, p .pos, span.pos, .text-pos {
-  color: $positive;
-  fill: $positive;
-  &:hover {
-    color: $positive;
-    fill: $positive;
-  }
-}
-
-p.neg, p .neg, span.neg, .text-neg {
-  color: $negative;
-  fill: $negative;
-  &:hover {
-    color: $negative;
-    fill: $negative;
-  }
-}
-
-p.muted, p .muted, span.muted, .text-muted {
-       color: lighten($text-dark, 26%);
-       fill: lighten($text-dark, 26%);
-    &.small, .small {
-      color: lighten($text-dark, 32%);
-      fill: lighten($text-dark, 32%);
-    }
-}
-
-p.primary, p .primary, span.primary, .text-primary {
-       color: $primary;
-       fill: $primary;
-  &:hover {
-    color: $primary;
-    fill: $primary;
-  }
-}
-
-p.secondary, p .secondary, span.secondary, .text-secondary {
-       color: $secondary;
-       fill: $secondary;
-  &:hover {
-    color: $secondary;
-    fill: $secondary;
-  }
-}
-
-.text-bookshelf {
-  color: $color-bookshelf;
-  fill: $color-bookshelf;
-  &:hover {
-    color: $color-bookshelf;
-    fill: $color-bookshelf;
-  }
-}
-.text-book {
-  color: $color-book;
-  fill: $color-book;
-  &:hover {
-    color: $color-book;
-    fill: $color-book;
-  }
-}
-.text-page {
-  color: $color-page;
-  fill: $color-page;
-  &:hover {
-    color: $color-page;
-    fill: $color-page;
-  }
-  &.draft {
-    color: $color-page-draft;
-    fill: $color-page-draft;
-  }
-  &.draft:hover {
-    color: $color-page-draft;
-    fill: $color-page-draft;
-  }
-}
-.text-chapter {
-  color: $color-chapter;
-  fill: $color-chapter;
-  &:hover {
-    color: $color-chapter;
-    fill: $color-chapter;
-  }
-}
-.faded .text-book:hover {
-  color: $color-book !important;
-  fill: $color-book !important;
-}
-.faded .text-chapter:hover {
-  color: $color-chapter !important;
-  fill: $color-chapter !important;
-}
-.faded .text-page:hover {
-  color: $color-page !important;
-  fill: $color-page !important;
-}
 
 span.highlight {
   //background-color: rgba($primary, 0.2);
@@ -435,10 +339,6 @@ span.sep {
 /**
   * Icons
   */
-i {
-  padding-right: $-xs;
-}
-
 .svg-icon {
   width: 1em;
   height: 1em;
@@ -446,5 +346,6 @@ i {
   position: relative;
   bottom: -0.105em;
   margin-right: $-xs;
+  pointer-events: none;
 }
 
index c758619d693c0d68533443104e2aa33eb1d58615..2dbf6916430c5e9485e17bfeb33b191d0aa0d460 100644 (file)
   text-align: center;
 }
 
+@include smaller-than($l) {
+  .mce-container-body.mce-flow-layout {
+    overflow-x: scroll;
+    white-space: nowrap;
+  }
+}
+
 .edit-area.flex > div > .mce-tinymce.mce-container.mce-panel {
   flex: 1 1 auto;
   display: flex !important;
index 006d1b3f08d105b40a54f2dc0f8ee2d11501be67..25b8e09b75f38e31a66acaf53c55eaa0c2b7f9e4 100644 (file)
@@ -1,14 +1,12 @@
 // Variables
 ///////////////
 
-// Sizes
-$max-width: 1400px;
-
 // Screen breakpoints
+$xxl: 1400px;
 $xl: 1100px;
 $ipad-width: 1028px; // Is actually 1024 but we go over to ensure functionality.
 $l: 1000px;
-$m: 800px;
+$m: 880px;
 $s: 600px;
 $xs: 400px;
 $xxs: 360px;
@@ -16,6 +14,9 @@ $screen-lg: 1200px;
 $screen-md: 992px;
 $screen-sm: 768px;
 
+// List of screen sizes
+$screen-sizes: (('xxs', $xxs), ('xs', $xs), ('s', $s), ('m', $m), ('l', $l), ('xl', $xl));
+
 // Spacing (Margins+Padding)
 $-xxxl: 64px;
 $-xxl: 48px;
@@ -26,6 +27,9 @@ $-s: 12px;
 $-xs: 6px;
 $-xxs: 3px;
 
+// List of our spacing sizes
+$spacing: (('none', 0), ('xxs', $-xxs), ('xs', $-xs), ('s', $-s), ('m', $-m), ('l', $-l), ('xl', $-xl), ('xxl', $-xxl));
+
 // Fonts
 $text: -apple-system, BlinkMacSystemFont,
 "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell",
@@ -33,8 +37,8 @@ $text: -apple-system, BlinkMacSystemFont,
 sans-serif;
 $mono: "Lucida Console", "DejaVu Sans Mono", "Ubunto Mono", Monaco, monospace;
 $heading: $text;
-$fs-m: 15px;
-$fs-s: 14px;
+$fs-m: 14px;
+$fs-s: 12px;
 
 // Colours
 $primary: #0288D1;
@@ -49,7 +53,7 @@ $primary-faded: rgba(21, 101, 192, 0.15);
 // Item Colors
 $color-bookshelf: #af5a5a;
 $color-book: #009688;
-$color-chapter: #ef7c3c;
+$color-chapter: #d7804a;
 $color-page: $primary;
 $color-page-draft: #9A60DA;
 
@@ -60,5 +64,5 @@ $text-light: #EEE;
 // Shadows
 $bs-light: 0 0 4px 1px #CCC;
 $bs-med: 0 1px 3px 1px rgba(76, 76, 76, 0.26);
-$bs-card: 0 1px 3px 1px rgba(76, 76, 76, 0.26), 0 1px 12px 0px rgba(76, 76, 76, 0.2);
+$bs-card: 0 1px 6px -1px rgba(0, 0, 0, 0.1);
 $bs-hover: 0 2px 2px 1px rgba(0,0,0,.13);
\ No newline at end of file
index cc90d5372e4af02023bb8d20c698253285f9c118..4cc782dc0e9d4881d533c55ebc518bef42dd4a6e 100644 (file)
@@ -1,8 +1,9 @@
 @import "variables";
 @import "mixins";
+@import "spacing";
 @import "html";
 @import "text";
-@import "grid";
+@import "layout";
 @import "blocks";
 @import "forms";
 @import "tables";
@@ -12,6 +13,9 @@
 
 body {
   font-family: 'DejaVu Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", "Oxygen", "Ubuntu", "Roboto", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
+  background-color: #FFF;
+  margin: 0;
+  padding: 0;
 }
 
 table {
@@ -19,6 +23,10 @@ table {
   border-collapse: collapse;
 }
 
+.page-content {
+  overflow: hidden;
+}
+
 // Prevent code block overflow on export
 pre {
   padding-left: 12px;
index c67f986424910242f3133edbae127e721ddd3853..44107f2d4d58fa6f59422a5c6181678c8d928cd4 100644 (file)
@@ -8,10 +8,6 @@ body {
   font-size: 12px;
 }
 
-.faded-small {
-  display: none;
-}
-
 .page-content {
   margin: 0 auto;
 }
index 49ef77f3938255cf55e45278d19ec2b3a2520c56..424074a0d270394bd44e70f5e615c055b792e703 100644 (file)
@@ -1,9 +1,11 @@
 @import "reset";
 @import "variables";
 @import "mixins";
+@import "spacing";
 @import "html";
 @import "text";
-@import "grid";
+@import "colors";
+@import "layout";
 @import "blocks";
 @import "buttons";
 @import "tables";
@@ -94,17 +96,6 @@ $loadingSize: 10px;
   }
 }
 
-
-// Search results
-.search-results > h3 a {
-  font-size: 0.66em;
-  color: $primary;
-  padding-left: $-m;
-  i {
-    padding-right: $-s;
-  }
-}
-
 // Back to top link
 $btt-size: 40px;
 [back-to-top] {
@@ -186,22 +177,28 @@ $btt-size: 40px;
     overflow-y: scroll;
     height: 400px;
     background-color: #EEEEEE;
+    margin-right: 0;
+    margin-left: 0;
+  }
+  .entity-list-item {
+    background-color: #FFF;
+  }
+  .entity-list-item p {
+    margin-bottom: 0;
+  }
+  .entity-list-item.selected {
+    background-color: rgba(0, 0, 0, 0.15) !important;
   }
   .loading {
     height: 400px;
     padding-top: $-l;
   }
-  .entity-list > p {
-    text-align: center;
-    padding-top: $-l;
-    font-size: 1.333em;
-  }
-  .entity-list > div {
-    padding-left: $-m;
-    padding-right: $-m;
-    background-color: #FFF;
-    transition: all ease-in-out 120ms;
-    cursor: pointer;
+  .entity-selector-add button {
+    margin: 0;
+    display: block;
+    width: 100%;
+    border: 0;
+    border-top: 1px solid #DDD;
   }
   &.compact {
     font-size: 10px;
@@ -211,12 +208,6 @@ $btt-size: 40px;
   }
 }
 
-.entity-list-item.selected {
-  h3, i, p ,a, span {
-    color: #EEE;
-  }
-}
-
 .scroll-box {
   max-height: 250px;
   overflow-y: scroll;
@@ -254,3 +245,39 @@ $btt-size: 40px;
   height:100%;
   z-index: 150;
 }
+
+.list-sort-container {
+  display: inline-block;
+  form {
+    display: inline-block;
+  }
+  .list-sort {
+    display: inline-grid;
+    margin-left: $-s;
+    grid-template-columns: 120px 40px;
+    border: 2px solid #DDD;
+    border-radius: 4px;
+  }
+  .list-sort-label {
+    font-weight: bold;
+    display: inline-block;
+    color: #888;
+  }
+  .list-sort-type {
+    text-align: left;
+  }
+  .list-sort-type, .list-sort-dir {
+    padding: $-xs $-s;
+    cursor: pointer;
+  }
+  .list-sort-dir {
+    border-left: 2px solid #DDD;
+    fill: #888;
+    .svg-icon {
+      transition: transform ease-in-out 120ms;
+    }
+    &:hover .svg-icon {
+      transform: rotate(180deg);
+    }
+  }
+}
\ No newline at end of file
diff --git a/resources/lang/cs/activities.php b/resources/lang/cs/activities.php
new file mode 100644 (file)
index 0000000..23fce81
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'vytvořená stránka',
+    'page_create_notification'    => 'Stránka byla úspěšně vytvořena',
+    'page_update'                 => 'aktualizovaná stránka',
+    'page_update_notification'    => 'Stránka byla úspěšně aktualizována',
+    'page_delete'                 => 'smazaná stránka',
+    'page_delete_notification'    => 'Stránka byla úspěšně smazána',
+    'page_restore'                => 'renovovaná stránka',
+    'page_restore_notification'   => 'Stránka byla úspěšně renovována',
+    'page_move'                   => 'přesunutá stránka',
+
+    // Chapters
+    'chapter_create'              => 'vytvořená kapitola',
+    'chapter_create_notification' => 'Kapitola byla úspěšně vytvořena',
+    'chapter_update'              => 'aktualizovaná kapitola',
+    'chapter_update_notification' => 'Kapitola byla úspěšně aktualizována',
+    'chapter_delete'              => 'smazaná kapitola',
+    'chapter_delete_notification' => 'Kapitola byla úspěšně smazána',
+    'chapter_move'                => 'přesunutá kapitola',
+
+    // Books
+    'book_create'                 => 'vytvořená kniha',
+    'book_create_notification'    => 'Kniha byla úspěšně vytvořena',
+    'book_update'                 => 'aktualizovaná kniha',
+    'book_update_notification'    => 'Kniha byla úspěšně aktualizována',
+    'book_delete'                 => 'smazaná kniha',
+    'book_delete_notification'    => 'Kniha byla úspěšně smazána',
+    'book_sort'                   => 'seřazená kniha',
+    'book_sort_notification'      => 'Kniha byla úspěšně seřazena',
+
+    // Bookshelves
+    'bookshelf_create'            => 'vytvořená knihovna',
+    'bookshelf_create_notification'    => 'Knihovna úspěšně vytvořena',
+    'bookshelf_update'                 => 'aktualizovaná knihovna',
+    'bookshelf_update_notification'    => 'Knihovna byla úspěšně aktualizována',
+    'bookshelf_delete'                 => 'smazaná knihovna',
+    'bookshelf_delete_notification'    => 'Knihovna byla úspěšně smazána',
+
+    // Other
+    'commented_on'                => 'okomentováno v',
+];
diff --git a/resources/lang/cs/auth.php b/resources/lang/cs/auth.php
new file mode 100644 (file)
index 0000000..69d6f0b
--- /dev/null
@@ -0,0 +1,65 @@
+<?php
+/**
+ * Authentication Language Lines
+ * The following language lines are used during authentication for various
+ * messages that we need to display to the user.
+ */
+return [
+
+    'failed' => 'Neplatné přihlašovací údaje.',
+    'throttle' => 'Příliš pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.',
+
+    // Login & Register
+    'sign_up' => 'Registrace',
+    'log_in' => 'Přihlášení',
+    'log_in_with' => 'Přihlásit přes :socialDriver',
+    'sign_up_with' => 'Registrovat se přes :socialDriver',
+    'logout' => 'Odhlásit',
+
+    'name' => 'Jméno',
+    'username' => 'Jméno účtu',
+    'email' => 'Email',
+    'password' => 'Heslo',
+    'password_confirm' => 'Potvrdit heslo',
+    'password_hint' => 'Musí mít víc než 5 znaků',
+    'forgot_password' => 'Zapomněli jste heslo?',
+    'remember_me' => 'Neodhlašovat',
+    'ldap_email_hint' => 'Zadejte email, který chcete přiřadit k tomuto účtu.',
+    'create_account' => 'Vytvořit účet',
+    'social_login' => 'Přihlášení přes sociální sítě',
+    'social_registration' => 'Registrace přes sociální sítě',
+    'social_registration_text' => 'Registrovat a přihlásit se přes jinou službu',
+
+    'register_thanks' => 'Díky za registraci!',
+    'register_confirm' => 'Zkontrolujte prosím váš email a klikněte na potvrzovací tlačítko pro dokončení registrace do :appName.',
+    'registrations_disabled' => 'Registrace jsou momentálně pozastaveny',
+    'registration_email_domain_invalid' => 'Registrace z této emailové domény nejsou povoleny.',
+    'register_success' => 'Díky za registraci! Jste registrovaní a přihlášení.',
+
+
+    // Password Reset
+    'reset_password' => 'Resetovat heslo',
+    'reset_password_send_instructions' => 'Zadejte vaší emailovou adresu a bude vám zaslán odkaz na resetování hesla.',
+    'reset_password_send_button' => 'Poslat odkaz pro reset hesla',
+    'reset_password_sent_success' => 'Odkaz na resetování hesla vám byl zaslán na :email.',
+    'reset_password_success' => 'Vaše heslo bylo úspěšně resetováno.',
+    'email_reset_subject' => 'Reset hesla do :appName',
+    'email_reset_text' => 'Tento email jste obdrželi, protože jsme dostali žádost o resetování vašeho hesla k účtu v :appName.',
+    'email_reset_not_requested' => 'Pokud jste o reset vašeho hesla nežádali, prostě tento dopis smažte a je to.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Potvrďte vaši emailovou adresu pro :appName',
+    'email_confirm_greeting' => 'Díky že jste se přidali do :appName!',
+    'email_confirm_text' => 'Prosíme potvrďte funkčnost vaší emailové adresy kliknutím na tlačítko níže:',
+    'email_confirm_action' => 'Potvrdit emailovou adresu',
+    'email_confirm_send_error' => 'Potvrzení emailové adresy je vyžadováno, ale systém vám nedokázal odeslat email. Kontaktujte správce aby to dal do kupy a potvrzovací email vám dorazil.',
+    'email_confirm_success' => 'Vaše emailová adresa byla potvrzena!',
+    'email_confirm_resent' => 'Email s žádostí o potvrzení vaší emailové adresy byl odeslán. Podívejte se do příchozí pošty.',
+
+    'email_not_confirmed' => 'Emailová adresa nebyla potvrzena',
+    'email_not_confirmed_text' => 'Vaše emailová adresa nebyla dosud potvrzena.',
+    'email_not_confirmed_click_link' => 'Klikněte na odkaz v emailu který jsme vám zaslali ihned po registraci.',
+    'email_not_confirmed_resend' => 'Pokud nemůžete nalézt email v příchozí poště, můžete si jej nechat poslat znovu pomocí formuláře níže.',
+    'email_not_confirmed_resend_button' => 'Znovu poslat email pro potvrzení emailové adresy',
+];
\ No newline at end of file
diff --git a/resources/lang/cs/common.php b/resources/lang/cs/common.php
new file mode 100644 (file)
index 0000000..b06e587
--- /dev/null
@@ -0,0 +1,59 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Storno',
+    'confirm' => 'Potvrdit',
+    'back' => 'Zpět',
+    'save' => 'Uložit',
+    'continue' => 'Pokračovat',
+    'select' => 'Zvolit',
+    'more' => 'Více',
+
+    // Form Labels
+    'name' => 'Jméno',
+    'description' => 'Popis',
+    'role' => 'Role',
+    'cover_image' => 'Obrázek na přebal',
+    'cover_image_description' => 'Obrázek by měl být asi 440 × 250px.',
+    
+    // Actions
+    'actions' => 'Akce',
+    'view' => 'Pohled',
+    'create' => 'Vytvořit',
+    'update' => 'Aktualizovat',
+    'edit' => 'Upravit',
+    'sort' => 'Řadit',
+    'move' => 'Přesunout',
+    'copy' => 'Kopírovat',
+    'reply' => 'Odpovědět',
+    'delete' => 'Smazat',
+    'search' => 'Hledat',
+    'search_clear' => 'Vyčistit hledání',
+    'reset' => 'Reset',
+    'remove' => 'Odstranit',
+    'add' => 'Přidat',
+
+    // Misc
+    'deleted_user' => 'Smazaný uživatel',
+    'no_activity' => 'Žádná aktivita k zobrazení',
+    'no_items' => 'Žádné položky nejsou k mání',
+    'back_to_top' => 'Zpět na začátek',
+    'toggle_details' => 'Ukázat detaily',
+    'toggle_thumbnails' => 'Ukázat náhledy',
+    'details' => 'Detaily',
+    'grid_view' => 'Zobrazit dlaždice',
+    'list_view' => 'Zobrazit seznam',
+    'default' => 'Výchozí',
+
+    // Header
+    'view_profile' => 'Ukázat profil',
+    'edit_profile' => 'Upravit profil',
+
+    // Email Content
+    'email_action_help' => 'Pokud se vám nedaří kliknout na tlačítko ":actionText", zkopírujte odkaz níže přímo do webového prohlížeče:',
+    'email_rights' => 'Všechna práva vyhrazena',
+];
\ No newline at end of file
diff --git a/resources/lang/cs/components.php b/resources/lang/cs/components.php
new file mode 100644 (file)
index 0000000..a62914f
--- /dev/null
@@ -0,0 +1,33 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Volba obrázku',
+    'image_all' => 'Vše',
+    'image_all_title' => 'Zobrazit všechny obrázky',
+    'image_book_title' => 'Zobrazit obrázky nahrané k této knize',
+    'image_page_title' => 'Zobrazit obrázky nahrané k této stránce',
+    'image_search_hint' => 'Hledat podle názvu obrázku',
+    'image_uploaded' => 'Nahráno :uploadedDate',
+    'image_load_more' => 'Načíst další',
+    'image_image_name' => 'Název obrázku',
+    'image_delete_used' => 'Tento obrázek je použit v následujících stránkách.',
+    'image_delete_confirm' => 'Stisknětě smazat ještě jednou pro potvrzení smazání tohoto obrázku.',
+    'image_select_image' => 'Zvolte obrázek',
+    'image_dropzone' => 'Přetáhněte sem obrázky myší nebo sem klikněte pro vybrání souboru.',
+    'images_deleted' => 'Obrázky smazány',
+    'image_preview' => 'Náhled obrázku',
+    'image_upload_success' => 'Obrázek byl úspěšně nahrán',
+    'image_update_success' => 'Podrobnosti o obrázku byly úspěšně aktualizovány',
+    'image_delete_success' => 'Obrázek byl úspěšně smazán',
+    'image_upload_remove' => 'Odstranit',
+
+    // Code Editor
+    'code_editor' => 'Upravit kód',
+    'code_language' => 'Jazyk kódu',
+    'code_content' => 'Obsah kódu',
+    'code_save' => 'Uložit kód',
+];
diff --git a/resources/lang/cs/entities.php b/resources/lang/cs/entities.php
new file mode 100644 (file)
index 0000000..dbf3485
--- /dev/null
@@ -0,0 +1,294 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Nedávno vytvořené',
+    'recently_created_pages' => 'Nedávno vytvořené stránky',
+    'recently_updated_pages' => 'Nedávno aktualizované stránky',
+    'recently_created_chapters' => 'Nedávno vytvořené kapitoly',
+    'recently_created_books' => 'Nedávno vytvořené knihy',
+    'recently_update' => 'Nedávno aktualizované',
+    'recently_viewed' => 'Nedávno prohlížené',
+    'recent_activity' => 'Nedávné činnosti',
+    'create_now' => 'Vytvořte jí',
+    'revisions' => 'Revize',
+    'meta_revision' => 'Revize #:revisionCount',
+    'meta_created' => 'Vytvořeno :timeLength',
+    'meta_created_name' => 'Vytvořeno :timeLength uživatelem :user',
+    'meta_updated' => 'Aktualizováno :timeLength',
+    'meta_updated_name' => 'Aktualizováno :timeLength uživatelem :user',
+    'entity_select' => 'Volba prvku',
+    'images' => 'Obrázky',
+    'my_recent_drafts' => 'Mé nedávné koncepty',
+    'my_recently_viewed' => 'Naposledy navštívené',
+    'no_pages_viewed' => 'Zatím jste nic neshlédli',
+    'no_pages_recently_created' => 'Zatím nebyly vytvořeny žádné stránky',
+    'no_pages_recently_updated' => 'Zatím nebyly aktualizovány žádné stránky',
+    'export' => 'Export',
+    'export_html' => 'Všeobjímající HTML',
+    'export_pdf' => 'PDF dokument',
+    'export_text' => 'Čistý text (txt)',
+
+    // Permissions and restrictions
+    'permissions' => 'Práva',
+    'permissions_intro' => 'Zaškrtnutím překryjete práva v uživatelských rolích nastavením níže.',
+    'permissions_enable' => 'Zapnout vlastní práva',
+    'permissions_save' => 'Uložit práva',
+
+    // Search
+    'search_results' => 'Výsledky hledání',
+    'search_total_results_found' => 'Nalezen :count výsledek|Nalezeny :count výsledky|Nalezeny :count výsledky|Nalezeny :count výsledky|Nalezeno :count výsledků',
+    'search_clear' => 'Vyčistit hledání',
+    'search_no_pages' => 'Žádná stránka neodpovídá hledanému výrazu',
+    'search_for_term' => 'Hledat :term',
+    'search_more' => 'Další výsledky',
+    'search_filters' => 'Filtry hledání',
+    'search_content_type' => 'Typ obsahu',
+    'search_exact_matches' => 'Musí obsahovat',
+    'search_tags' => 'Hledat štítky (tagy)',
+    'search_options' => 'Volby',
+    'search_viewed_by_me' => 'Shlédnuto mnou',
+    'search_not_viewed_by_me' => 'Neshlédnuto mnou',
+    'search_permissions_set' => 'Sada práv',
+    'search_created_by_me' => 'Vytvořeno mnou',
+    'search_updated_by_me' => 'Aktualizováno',
+    'search_date_options' => 'Volby datumu',
+    'search_updated_before' => 'Aktualizováno před',
+    'search_updated_after' => 'Aktualizováno po',
+    'search_created_before' => 'Vytvořeno před',
+    'search_created_after' => 'Vytvořeno po',
+    'search_set_date' => 'Datum',
+    'search_update' => 'Hledat znovu',
+
+    // Shelves
+    'shelf' => 'Knihovna',
+    'shelves' => 'Knihovny',
+    'shelves_long' => 'Knihovny',
+    'shelves_empty' => 'Žádné knihovny nebyly vytvořeny',
+    'shelves_create' => 'Vytvořit novou knihovnu',
+    'shelves_popular' => 'Populární knihovny',
+    'shelves_new' => 'Nové knihovny',
+    'shelves_popular_empty' => 'Nejpopulárnější knihovny se objeví zde.',
+    'shelves_new_empty' => 'Nejnovější knihovny se objeví zde.',
+    'shelves_save' => 'Uložit knihovnu',
+    'shelves_books' => 'Knihy v této knihovně',
+    'shelves_add_books' => 'Přidat knihy do knihovny',
+    'shelves_drag_books' => 'Knihu přidáte jejím přetažením sem.',
+    'shelves_empty_contents' => 'Tato knihovna neobsahuje žádné knihy',
+    'shelves_edit_and_assign' => 'Pro přidáni knih do knihovny stiskněte úprvy.',
+    'shelves_edit_named' => 'Upravit knihovnu :name',
+    'shelves_edit' => 'Upravit knihovnu',
+    'shelves_delete' => 'Smazat knihovnu',
+    'shelves_delete_named' => 'Smazat knihovnu :name',
+    'shelves_delete_explain' => "Chystáte se smazat knihovnu ':name'. Knihy v ní obsažené zůstanou zachovány.",
+    'shelves_delete_confirmation' => 'Opravdu chcete smazat tuto knihovnu?',
+    'shelves_permissions' => 'Práva knihovny',
+    'shelves_permissions_updated' => 'Práva knihovny byla aktualizována',
+    'shelves_permissions_active' => 'Účinná práva knihovny',
+    'shelves_copy_permissions_to_books' => 'Přenést práva na knihy',
+    'shelves_copy_permissions' => 'Zkopírovat práva',
+    'shelves_copy_permissions_explain' => 'Práva knihovny budou aplikována na všechny knihy v ní obsažené. Před použitím se ujistěte, že jste uložili změny práv knihovny.',
+    'shelves_copy_permission_success' => 'Práva knihovny přenesena na knihy (celkem :count)',
+
+    // Books
+    'book' => 'Kniha',
+    'books' => 'Knihy',
+    'x_books' => ':count Kniha|:count Knihy|:count Knihy|:count Knihy|:count Knih',
+    'books_empty' => 'Žádné knihy nebyly vytvořeny',
+    'books_popular' => 'Populární knihy',
+    'books_recent' => 'Nedávné knihy',
+    'books_new' => 'Nové knihy',
+    'books_popular_empty' => 'Zde budou zobrazeny nejpopulárnější knihy.',
+    'books_new_empty' => 'Zde budou zobrazeny nově vytvořené knihy.',
+    'books_create' => 'Vytvořit novou knihu',
+    'books_delete' => 'Smazat knihu',
+    'books_delete_named' => 'Smazat knihu :bookName',
+    'books_delete_explain' => 'Kniha \':bookName\' bude smazána. Všechny její stránky a kapitoly budou taktéž smazány.',
+    'books_delete_confirmation' => 'Opravdu chcete tuto knihu smazat.',
+    'books_edit' => 'Upravit knihu',
+    'books_edit_named' => 'Upravit knihu :bookName',
+    'books_form_book_name' => 'Název knihy',
+    'books_save' => 'Uložit knihu',
+    'books_permissions' => 'Práva knihy',
+    'books_permissions_updated' => 'Práva knihy upravena',
+    'books_empty_contents' => 'V této knize nebyly vytvořeny žádné stránky ani kapitoly.',
+    'books_empty_create_page' => 'Vytvořit novou stránku',
+    'books_empty_or' => 'nebo',
+    'books_empty_sort_current_book' => 'Seřadit tuto knihu',
+    'books_empty_add_chapter' => 'Přidat kapitolu',
+    'books_permissions_active' => 'Účinná práva knihy',
+    'books_search_this' => 'Prohledat tuto knihu',
+    'books_navigation' => 'Obsah knihy',
+    'books_sort' => 'Seřadit obsah knihy',
+    'books_sort_named' => 'Seřadit knihu :bookName',
+    'books_sort_show_other' => 'Ukázat ostatní knihy',
+    'books_sort_save' => 'Uložit nové pořadí',
+
+    // Chapters
+    'chapter' => 'Kapitola',
+    'chapters' => 'Kapitoly',
+    'x_chapters' => ':count kapitola|:count kapitoly|:count kapitoly|:count kapitoly|:count kapitol',
+    'chapters_popular' => 'Populární kapitoly',
+    'chapters_new' => 'Nová kapitola',
+    'chapters_create' => 'Vytvořit novou kapitolu',
+    'chapters_delete' => 'Smazat kapitolu',
+    'chapters_delete_named' => 'Smazat kapitolu :chapterName',
+    'chapters_delete_explain' => 'Kapitola \':chapterName\' bude smazána. Všechny stránky v ní obsažené budou přesunuty přímo pod samotnou knihu.',
+    'chapters_delete_confirm' => 'Opravdu chcete tuto kapitolu smazat?',
+    'chapters_edit' => 'Upravit kapitolu',
+    'chapters_edit_named' => 'Upravit kapitolu :chapterName',
+    'chapters_save' => 'Uložit kapitolu',
+    'chapters_move' => 'Přesunout kapitolu',
+    'chapters_move_named' => 'Přesunout kapitolu :chapterName',
+    'chapter_move_success' => 'Kapitola přesunuta do knihy :bookName',
+    'chapters_permissions' => 'Práva kapitoly',
+    'chapters_empty' => 'Tato kapitola neobsahuje žádné stránky',
+    'chapters_permissions_active' => 'Účinná práva kapitoly',
+    'chapters_permissions_success' => 'Práva kapitoly aktualizována',
+    'chapters_search_this' => 'Prohledat tuto kapitolu',
+
+    // Pages
+    'page' => 'Stránka',
+    'pages' => 'Stránky',
+    'x_pages' => ':count strana|:count strany|:count strany|:count strany|:count stran',
+    'pages_popular' => 'Populární stránky',
+    'pages_new' => 'Nová stránka',
+    'pages_attachments' => 'Přílohy',
+    'pages_navigation' => 'Obsah stránky',
+    'pages_delete' => 'Smazat stránku',
+    'pages_delete_named' => 'Smazat stránku :pageName',
+    'pages_delete_draft_named' => 'Smazat koncept stránky :pageName',
+    'pages_delete_draft' => 'Smazat koncept stránky',
+    'pages_delete_success' => 'Stránka smazána',
+    'pages_delete_draft_success' => 'Koncept stránky smazán',
+    'pages_delete_confirm' => 'Opravdu chcete tuto stránku smazat?',
+    'pages_delete_draft_confirm' => 'Opravdu chcete tento koncept stránky smazat?',
+    'pages_editing_named' => 'Úpravy stránky :pageName',
+    'pages_edit_toggle_header' => 'Ukázat hlavičku',
+    'pages_edit_save_draft' => 'Uložit koncept',
+    'pages_edit_draft' => 'Upravit koncept stránky',
+    'pages_editing_draft' => 'Úpravy konceptu',
+    'pages_editing_page' => 'Úpravy stránky',
+    'pages_edit_draft_save_at' => 'Koncept uložen v ',
+    'pages_edit_delete_draft' => 'Smazat koncept',
+    'pages_edit_discard_draft' => 'Zahodit koncept',
+    'pages_edit_set_changelog' => 'Zadat komentář ke změnám',
+    'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli.',
+    'pages_edit_enter_changelog' => 'Vložit komentáře ke změnám',
+    'pages_save' => 'Uložit stránku',
+    'pages_title' => 'Nadpis stránky',
+    'pages_name' => 'Název stránky',
+    'pages_md_editor' => 'Editor',
+    'pages_md_preview' => 'Náhled',
+    'pages_md_insert_image' => 'Vložit obrázek',
+    'pages_md_insert_link' => 'Vložit odkaz na prvek',
+    'pages_md_insert_drawing' => 'Vložit kresbu',
+    'pages_not_in_chapter' => 'Stránka není součástí žádné kapitoly',
+    'pages_move' => 'Přesunout stránku',
+    'pages_move_success' => 'Stránka přesunuta do ":parentName"',
+    'pages_copy' => 'Kopírovat stránku',
+    'pages_copy_desination' => 'Cíl kopírování',
+    'pages_copy_success' => 'Stránka byla úspěšně zkopírována',
+    'pages_permissions' => 'Práva stránky',
+    'pages_permissions_success' => 'Práva stránky aktualizována',
+    'pages_revision' => 'Revize',
+    'pages_revisions' => 'Revize stránky',
+    'pages_revisions_named' => 'Revize stránky :pageName',
+    'pages_revision_named' => 'Revize stránky :pageName',
+    'pages_revisions_created_by' => 'Vytvořeno uživatelem',
+    'pages_revisions_date' => 'Datum revize',
+    'pages_revisions_number' => '#',
+    'pages_revisions_changelog' => 'Komentáře změn',
+    'pages_revisions_changes' => 'Změny',
+    'pages_revisions_current' => 'Aktuální verze',
+    'pages_revisions_preview' => 'Náhled',
+    'pages_revisions_restore' => 'Renovovat',
+    'pages_revisions_none' => 'Tato stránka nemá žádné revize',
+    'pages_copy_link' => 'Zkopírovat odkaz',
+    'pages_edit_content_link' => 'Upravit obsah',
+    'pages_permissions_active' => 'Účinná práva stránky',
+    'pages_initial_revision' => 'První vydání',
+    'pages_initial_name' => 'Nová stránka',
+    'pages_editing_draft_notification' => 'Právě upravujete koncept, který byl uložen před :timeDiff.',
+    'pages_draft_edited_notification' => 'Tato stránka se od té doby změnila. Je doporučeno aktuální koncept zahodit.',
+    'pages_draft_edit_active' => [
+        'start_a' => 'Uživatelé začali upravovat tuto stránku (celkem :count)',
+        'start_b' => 'Uživatel :userName začal upravovat tuto stránku',
+        'time_a' => 'od doby, kdy byla tato stránky naposledy aktualizována',
+        'time_b' => 'v posledních minutách (:minCount min.)',
+        'message' => ':start :time. Dávejte pozor abyste nepřepsali změny ostatním!',
+    ],
+    'pages_draft_discarded' => 'Koncept zahozen. Editor nyní obsahuje aktuální verzi stránky.',
+    'pages_specific' => 'Konkrétní stránka',
+
+    // Editor Sidebar
+    'page_tags' => 'Štítky stránky',
+    'chapter_tags' => 'Štítky kapitoly',
+    'book_tags' => 'Štítky knihy',
+    'shelf_tags' => 'Štítky knihovny',
+    'tag' => 'Štítek',
+    'tags' =>  'Štítky',
+    'tag_value' => 'Hodnota Štítku (volitelné)',
+    'tags_explain' => "Přidejte si štítky pro lepší kategorizaci knih. \n Štítky mohou nést i hodnotu pro detailnější klasifikaci.",
+    'tags_add' => 'Přidat další štítek',
+    'attachments' => 'Přílohy',
+    'attachments_explain' => 'Nahrajte soubory nebo připojte odkazy, které se zobrazí na stránce. Budou k nalezení v postranní liště.',
+    'attachments_explain_instant_save' => 'Změny zde provedené se okamžitě ukládají.',
+    'attachments_items' => 'Připojené položky',
+    'attachments_upload' => 'Nahrát soubor',
+    'attachments_link' => 'Připojit odkaz',
+    'attachments_set_link' => 'Nastavit odkaz',
+    'attachments_delete_confirm' => 'Stiskněte smazat znovu pro potvrzení smazání.',
+    'attachments_dropzone' => 'Přetáhněte sem soubory myší nebo sem kliknětě pro vybrání souboru.',
+    'attachments_no_files' => 'Žádné soubory nebyli nahrány',
+    'attachments_explain_link' => 'Můžete pouze připojit odkaz pokud nechcete nahrávat soubor přímo. Může to být odkaz na jinou stránku nebo na soubor v cloudu.',
+    'attachments_link_name' => 'Název odkazu',
+    'attachment_link' => 'Odkaz na přílohu',
+    'attachments_link_url' => 'Odkaz na soubor',
+    'attachments_link_url_hint' => 'URL stránky nebo souboru',
+    'attach' => 'Připojit',
+    'attachments_edit_file' => 'Upravit soubor',
+    'attachments_edit_file_name' => 'Název souboru',
+    'attachments_edit_drop_upload' => 'Přetáhněte sem soubor myší nebo klikněte pro nahrání nového a následné přepsání starého.',
+    'attachments_order_updated' => 'Pořadí příloh aktualizováno',
+    'attachments_updated_success' => 'Podrobnosti příloh aktualizovány',
+    'attachments_deleted' => 'Příloha byla smazána',
+    'attachments_file_uploaded' => 'Soubor byl úspěšně nahrán',
+    'attachments_file_updated' => 'Soubor byl úspěšně aktualizován',
+    'attachments_link_attached' => 'Odkaz úspěšně přiložen ke stránce',
+
+    // Profile View
+    'profile_user_for_x' => 'Uživatelem již :time',
+    'profile_created_content' => 'Vytvořený obsah',
+    'profile_not_created_pages' => ':userName nevytvořil/a žádný obsah',
+    'profile_not_created_chapters' => ':userName nevytvořil/a žádné kapitoly',
+    'profile_not_created_books' => ':userName nevytvořil/a žádné knihy',
+
+    // Comments
+    'comment' => 'Komentář',
+    'comments' => 'Komentáře',
+    'comment_add' => 'Přidat komentář',
+    'comment_placeholder' => 'Zanechat komentář zde',
+    'comment_count' => '{0} Bez komentářů|{1} 1 komentář|[2,4] :count komentáře|[5,*] :count komentářů',
+    'comment_save' => 'Uložit komentář',
+    'comment_saving' => 'Ukládání komentáře...',
+    'comment_deleting' => 'Mazání komentáře...',
+    'comment_new' => 'Nový komentář',
+    'comment_created' => 'komentováno :createDiff',
+    'comment_updated' => 'Aktualizováno :updateDiff uživatelem :username',
+    'comment_deleted_success' => 'Komentář smazán',
+    'comment_created_success' => 'Komentář přidán',
+    'comment_updated_success' => 'Komentář aktualizován',
+    'comment_delete_confirm' => 'Opravdu chcete smazat tento komentář?',
+    'comment_in_reply_to' => 'Odpověď na :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Opravdu chcete smazat tuto revizi?',
+    'revision_delete_success' => 'Revize smazána',
+    'revision_cannot_delete_latest' => 'Nelze smazat poslední revizi.'
+
+];
\ No newline at end of file
diff --git a/resources/lang/cs/errors.php b/resources/lang/cs/errors.php
new file mode 100644 (file)
index 0000000..bd9c62b
--- /dev/null
@@ -0,0 +1,84 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Nemáte povolení přistupovat na dotazovanou stránku.',
+    'permissionJson' => 'Nemáte povolení k provedení požadované akce.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Uživatel s emailem :email již existuje ale s jinými přihlašovacími údaji.',
+    'email_already_confirmed' => 'Emailová adresa již byla potvrzena. Zkuste se přihlásit.',
+    'email_confirmation_invalid' => 'Tento potvrzovací odkaz již neplatí nebo už byl použit. Zkuste prosím registraci znovu.',
+    'email_confirmation_expired' => 'Potvrzovací odkaz už neplatí, email s novým odkazem už byl poslán.',
+    'ldap_fail_anonymous' => 'Přístup k adresáři LDAP jako anonymní uživatel (anonymous bind) selhal',
+    'ldap_fail_authed' => 'Přístup k adresáři LDAP pomocí zadaného jména (dn) a hesla selhal',
+    'ldap_extension_not_installed' => 'Není nainstalováno rozšíření LDAP pro PHP',
+    'ldap_cannot_connect' => 'Nelze se připojit k adresáři LDAP. Prvotní připojení selhalo.',
+    'social_no_action_defined' => 'Nebyla zvolena žádá akce',
+    'social_login_bad_response' => "Nastala chyba během přihlašování přes :socialAccount \n:error",
+    'social_account_in_use' => 'Tento účet na :socialAccount se již používá. Pokuste se s ním přihlásit volbou Přihlásit přes :socialAccount.',
+    'social_account_email_in_use' => 'Emailová adresa :email se již používá. Pokud máte již máte náš účet, můžete si jej propojit se svým účtem na :socialAccount v nastavení vašeho profilu.',
+    'social_account_existing' => 'Tento účet na :socialAccount je již propojen s vaším profilem zde.',
+    'social_account_already_used_existing' => 'Tento účet na :socialAccount je již používán jiným uživatelem.',
+    'social_account_not_used' => 'Tento účet na :socialAccount není spřažen s žádným uživatelem. Prosím přiřaďtě si jej v nastavení svého profilu.',
+    'social_account_register_instructions' => 'Pokud ještě nemáte náš účet, můžete se zaregistrovat pomocí vašeho účtu na :socialAccount.',
+    'social_driver_not_found' => 'Doplněk pro tohoto správce identity nebyl nalezen.',
+    'social_driver_not_configured' => 'Nastavení vašeho účtu na :socialAccount není správné. :socialAccount musí mít vaše svolení pro naší aplikaci vás přihlásit.',
+
+    // System
+    'path_not_writable' => 'Nelze zapisovat na cestu k souboru :filePath. Zajistěte aby se dalo nahrávat na server.',
+    'cannot_get_image_from_url' => 'Nelze získat obrázek z adresy :url',
+    'cannot_create_thumbs' => 'Server nedokáže udělat náhledy. Zkontrolujte, že rozšíření GD pro PHP je nainstalováno.',
+    'server_upload_limit' => 'Server nepovoluje nahrávat tak veliké soubory. Zkuste prosím menší soubor.',
+    'uploaded'  => 'Server nepovoluje nahrávat tak veliké soubory. Zkuste prosím menší soubor.', //TODO to je nějaký podezřelý
+    'image_upload_error' => 'Nastala chyba během nahrávání souboru',
+    'image_upload_type_error' => 'Typ nahrávaného obrázku je neplatný.',
+    'file_upload_timeout' => 'Nahrávání souboru trvalo příliš dlouho a tak bylo ukončeno.',
+
+    // Attachments
+    'attachment_page_mismatch' => 'Došlo ke zmatení stránky během nahrávání přílohy.',
+    'attachment_not_found' => 'Příloha nenalezena',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Nepovedlo se uložit koncept. Než stránku uložíte, ujistěte se, že jste připojeni k internetu.',
+    'page_custom_home_deletion' => 'Nelze smazat tuto stránku, protože je nastavena jako uvítací stránka.',
+
+    // Entities
+    'entity_not_found' => 'Prvek nenalezen',
+    'bookshelf_not_found' => 'Knihovna nenalezena',
+    'book_not_found' => 'Kniha nenalezena',
+    'page_not_found' => 'Stránka nenalezena',
+    'chapter_not_found' => 'Kapitola nenalezena',
+    'selected_book_not_found' => 'Vybraná kniha nebyla nalezena',
+    'selected_book_chapter_not_found' => 'Zvolená kniha nebo kapitola nebyla nalezena',
+    'guests_cannot_save_drafts' => 'Návštěvníci z řad veřejnosti nemohou ukládat koncepty.',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Nemůžete smazat posledního administrátora',
+    'users_cannot_delete_guest' => 'Uživatele host není možno smazat',
+
+    // Roles
+    'role_cannot_be_edited' => 'Tuto roli nelze editovat',
+    'role_system_cannot_be_deleted' => 'Toto je systémová role a nelze jí smazat.',
+    'role_registration_default_cannot_delete' => 'Tuto roli nelze smazat dokud je nastavená jako výchozí role pro registraci nových uživatelů.',
+    'role_cannot_remove_only_admin' => 'Tento uživatel má roli administrátora. Přiřaďte roli administrátora někomu jinému než jí odeberete zde.',
+
+    // Comments
+    'comment_list' => 'Při dotahování komentářů nastala chyba.',
+    'cannot_add_comment_to_draft' => 'Nemůžete přidávat komentáře ke konceptu.',
+    'comment_add' => 'Při přidávání / aktualizaci komentáře nastala chyba.',
+    'comment_delete' => 'Při mazání komentáře nastala chyba.',
+    'empty_comment' => 'Nemůžete přidat prázdný komentář.', //This has a deep thinking value
+
+    // Error pages
+    '404_page_not_found' => 'Stránka nenalezena',
+    'sorry_page_not_found' => 'Omlouváme se, ale stránka, kterou hledáte nebyla nalezena.',
+    'return_home' => 'Návrat domů',
+    'error_occurred' => 'Nastala chyba',
+    'app_down' => ':appName je momentálně vypnutá',
+    'back_soon' => 'Brzy naběhne.',
+
+];
diff --git a/resources/lang/cs/pagination.php b/resources/lang/cs/pagination.php
new file mode 100644 (file)
index 0000000..de98215
--- /dev/null
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Pagination Language Lines
+ * The following language lines are used by the paginator library to build
+ * the simple pagination links.
+ */
+return [
+
+    'previous' => '&laquo; Pøedchozí',
+    'next'     => 'Dal\9aí &raquo;',
+
+];
diff --git a/resources/lang/cs/passwords.php b/resources/lang/cs/passwords.php
new file mode 100644 (file)
index 0000000..0200163
--- /dev/null
@@ -0,0 +1,15 @@
+<?php
+/**
+ * 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.
+ */
+return [
+
+    'password' => 'Heslo musí být alespoň 6 znaků dlouhé a shodovat se v obou polích.',
+    'user' => "Nemůžeme najít uživatele se zadanou emailovou adresou.",
+    'token' => 'Tento odkaz pro reset hesla je neplatný.',
+    'sent' => 'Poslali jsme vám odkaz pro reset hesla!',
+    'reset' => 'Vaše heslo bylo resetováno!',
+
+];
diff --git a/resources/lang/cs/settings.php b/resources/lang/cs/settings.php
new file mode 100644 (file)
index 0000000..1881dff
--- /dev/null
@@ -0,0 +1,117 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Nastavení',
+    'settings_save' => 'Uložit nastavení',
+    'settings_save_success' => 'Nastavení bylo uloženo',
+
+    // App Settings
+    'app_settings' => 'Nastavení aplikace',
+    'app_name' => 'Název aplikace',
+    'app_name_desc' => 'Název se bude zobrazovat v záhlaví této aplikace a v odesílaných emailech.',
+    'app_name_header' => 'Zobrazovát název aplikace v záhlaví?',
+    'app_public_viewing' => 'Povolit prohlížení veřejností?',
+    'app_secure_images' => 'Nahrávat obrázky neveřejně a zabezpečeně?',
+    'app_secure_images_desc' => 'Z výkonnostních důvodů jsou všechny obrázky veřejné. Tato volba přidá do adresy obrázku náhodné číslo, aby nikdo neodhadnul adresu obrázku. Zajistěte ať adresáře nikomu nezobrazují seznam souborů.',
+    'app_editor' => 'Editor stránek',
+    'app_editor_desc' => 'Zvolte který editor budou užívat všichni uživatelé k úpravě stránek.',
+    'app_custom_html' => 'Vlastní HTML kód pro sekci hlavičky (<head>).',
+    'app_custom_html_desc' => 'Cokoliv sem napíšete bude přidáno na konec sekce <head> v každém místě této aplikace. To se hodí pro přidávání nebo změnu CSS stylů nebo přidání kódu pro analýzu používání (např.: google analytics.).',
+    'app_logo' => 'Logo aplikace',
+    'app_logo_desc' => 'Obrázek by měl mít 43 pixelů na výšku. <br>Větší obrázky zmenšíme na tuto velikost.',
+    'app_primary_color' => 'Hlavní barva aplikace',
+    'app_primary_color_desc' => 'Zápis by měl být hexa (#aabbcc). <br>Pro základní barvu nechte pole prázdné.',
+    'app_homepage' => 'Úvodní stránka aplikace',
+    'app_homepage_desc' => 'Zvolte pohled který se objeví jako úvodní stránka po přihlášení. Pokud zvolíte stránku, její specifická oprávnění budou ignorována (výjimka z výjimky 😜).',
+    'app_homepage_select' => 'Zvolte stránku',
+    'app_disable_comments' => 'Zakázání komentářů',
+    'app_disable_comments_desc' => 'Zakáže komentáře napříč všemi stránkami. Existující komentáře se přestanou zobrazovat.',
+
+    // Registration Settings
+    'reg_settings' => 'Nastavení registrace',
+    'reg_allow' => 'Povolit registrace?',
+    'reg_default_role' => 'Role přiřazená po registraci',
+    'reg_confirm_email' => 'Vyžadovat ověření emailové adresy?',
+    'reg_confirm_email_desc' => 'Pokud zapnete omezení emailové domény, tak bude ověřování emailové adresy vyžadováno vždy.',
+    'reg_confirm_restrict_domain' => 'Omezit registraci podle domény',
+    'reg_confirm_restrict_domain_desc' => 'Zadejte emailové domény, kterým bude povolena registrace uživatelů. Oddělujete čárkou. Uživatelům bude odeslán email s odkazem pro potvrzení vlastnictví emailové adresy. Bez potvrzení nebudou moci aplikaci používat. <br> Pozn.: Uživatelé si mohou emailovou adresu změnit po úspěšné registraci.',
+    'reg_confirm_restrict_domain_placeholder' => 'Žádná omezení nebyla nastvena',
+
+    // Maintenance settings
+    'maint' => 'Údržba',
+    'maint_image_cleanup' => 'Pročistění obrázků',
+    'maint_image_cleanup_desc' => "Prohledá stránky a jejich revize, aby zjistil, které obrázky a kresby jsou momentálně používány a které jsou zbytečné. Zajistěte plnou zálohu databáze a obrázků než se do toho pustíte.",
+    'maint_image_cleanup_ignore_revisions' => 'Ignorovat obrázky v revizích',
+    'maint_image_cleanup_run' => 'Spustit pročištění',
+    'maint_image_cleanup_warning' => 'Nalezeno :count potenciálně nepoužitých obrázků. Jste si jistí, že je chcete smazat?',
+       
+    'maint_image_cleanup_success' => 'Potenciálně nepoužité obrázky byly smazány. Celkem :count.',
+    'maint_image_cleanup_nothing_found' => 'Žádné potenciálně nepoužité obrázky nebyly nalezeny. Nic nebylo smazáno.',
+
+    // Role Settings
+    'roles' => 'Role',
+    'role_user_roles' => 'Uživatelské role',
+    'role_create' => 'Vytvořit novou roli',
+    'role_create_success' => 'Role byla úspěšně vytvořena',
+    'role_delete' => 'Smazat roli',
+    'role_delete_confirm' => 'Role \':roleName\' bude smazána.',
+    'role_delete_users_assigned' => 'Role je přiřazena :userCount uživatelům. Pokud jim chcete náhradou přidělit jinou roli, zvolte jednu z následujících.',
+    'role_delete_no_migration' => "Nepřiřazovat uživatelům náhradní roli",
+    'role_delete_sure' => 'Opravdu chcete tuto roli smazat?',
+    'role_delete_success' => 'Role byla úspěšně smazána',
+    'role_edit' => 'Upravit roli',
+    'role_details' => 'Detaily role',
+    'role_name' => 'Název role',
+    'role_desc' => 'Stručný popis role',
+    'role_external_auth_id' => 'Přihlašovací identifikátory třetích stran',
+    'role_system' => 'Systémová oprávnění',
+    'role_manage_users' => 'Správa úživatelů',
+    'role_manage_roles' => 'Správa rolí a jejich práv',
+    'role_manage_entity_permissions' => 'Správa práv všech knih, kapitol a stránek',
+    'role_manage_own_entity_permissions' => 'Správa práv vlastních knih, kapitol a stránek',
+    'role_manage_settings' => 'Správa nastavení aplikace',
+    'role_asset' => 'Práva děl',
+    'role_asset_desc' => 'Tato práva řídí přístup k dílům v rámci systému. Specifická práva na knihách, kapitolách a stránkách překryjí tato nastavení.',
+    'role_asset_admins' => 'Administrátoři automaticky dostávají přístup k veškerému obsahu, ale tyto volby mohou ukázat nebo skrýt volby v uživatelském rozhraní.',
+    'role_all' => 'Vše',
+    'role_own' => 'Vlastní',
+    'role_controlled_by_asset' => 'Řídí se dílem do kterého jsou nahrávány',
+    'role_save' => 'Uloži roli',
+    'role_update_success' => 'Role úspěšně aktualizována',
+    'role_users' => 'Uživatelé mající tuto roli',
+    'role_users_none' => 'Žádný uživatel nemá tuto roli.',
+
+    // Users
+    'users' => 'Uživatelé',
+    'user_profile' => 'Profil uživatele',
+    'users_add_new' => 'Přidat nového uživatele',
+    'users_search' => 'Vyhledávání uživatelů',
+    'users_role' => 'Uživatelské role',
+    'users_external_auth_id' => 'Přihlašovací identifikátory třetích stran',
+    'users_password_warning' => 'Vyplňujte pouze v případě, že chcete heslo změnit:',
+    'users_system_public' => 'Symbolizuje libovolného veřejného návštěvníka, který navštívil vaší aplikaci. Nelze ho použít k přihlášení ale je přiřazen automaticky veřejnosti.',
+    'users_delete' => 'Smazat uživatele',
+    'users_delete_named' => 'Smazat uživatele :userName',
+    'users_delete_warning' => 'Uživatel \':userName\' bude úplně smazán ze systému.',
+    'users_delete_confirm' => 'Opravdu chcete tohoto uživatele smazat?',
+    'users_delete_success' => 'Uživatel byl úspěšně smazán',
+    'users_edit' => 'Upravit uživatele',
+    'users_edit_profile' => 'Upravit profil',
+    'users_edit_success' => 'Uživatel byl úspěšně aktualizován',
+    'users_avatar' => 'Uživatelský obrázek',
+    'users_avatar_desc' => 'Obrázek by měl být čtverec 256 pixelů široký. Bude oříznut do kruhu.',
+    'users_preferred_language' => 'Upřednostňovaný jazyk',
+    'users_social_accounts' => 'Přidružené účty ze sociálních sítí',
+    'users_social_accounts_info' => 'Zde můžete přidat vaše účty ze sociálních sítí pro pohodlnější přihlašování. Zrušení přidružení zde neznamená, že tato aplikace pozbude práva číst detaily z vašeho účtu. Zakázat této aplikaci přístup k detailům vašeho účtu musíte přímo ve vašem profilu na dané sociální síti.',     
+    
+    'users_social_connect' => 'Přidružit účet',
+    'users_social_disconnect' => 'Zrušit přidružení',
+    'users_social_connected' => 'Účet :socialAccount byl úspěšně přidružen k vašemu profilu.',
+    'users_social_disconnected' => 'Přidružení účtu :socialAccount k vašemu profilu bylo úspěšně zrušeno.'
+];
diff --git a/resources/lang/cs/validation.php b/resources/lang/cs/validation.php
new file mode 100644 (file)
index 0000000..3e47fb0
--- /dev/null
@@ -0,0 +1,152 @@
+<?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.
+    |
+    */
+
+    'accepted'             => ':attribute musí být přijat.',
+    'active_url'           => ':attribute není platnou URL adresou.',
+    'after'                => ':attribute musí být datum po :date.',
+    'after_or_equal'       => ':attribute musí být datum :date nebo pozdější.',
+    'alpha'                => ':attribute může obsahovat pouze písmena.',
+    'alpha_dash'           => ':attribute může obsahovat pouze písmena, číslice, pomlčky a podtržítka. České znaky (á, é, í, ó, ú, ů, ž, š, č, ř, ď, ť, ň) nejsou podporovány.',
+    'alpha_num'            => ':attribute může obsahovat pouze písmena a číslice.',
+    'array'                => ':attribute musí být pole.',
+    'before'               => ':attribute musí být datum před :date.',
+    'before_or_equal'      => 'Datum :attribute musí být před nebo rovno :date.',
+    'between'              => [
+        'numeric' => ':attribute musí být hodnota mezi :min a :max.',
+        'file'    => ':attribute musí být větší než :min a menší než :max Kilobytů.',
+        'string'  => ':attribute musí být delší než :min a kratší než :max znaků.',
+        'array'   => ':attribute musí obsahovat nejméně :min a nesmí obsahovat více než :max prvků.',
+    ],
+    'boolean'              => ':attribute musí být true nebo false',
+    'confirmed'            => ':attribute nesouhlasí.',
+    'date'                 => ':attribute musí být platné datum.',
+    'date_equals'          => 'The :attribute must be a date equal to :date.',
+    'date_format'          => ':attribute není platný formát data podle :format.',
+    'different'            => ':attribute a :other se musí lišit.',
+    'digits'               => ':attribute musí být :digits pozic dlouhé.',
+    'digits_between'       => ':attribute musí být dlouhé nejméně :min a nejvíce :max pozic.',
+    'dimensions'           => ':attribute má neplatné rozměry.',
+    'distinct'             => ':attribute má duplicitní hodnotu.',
+    'email'                => ':attribute není platný formát.',
+    'exists'               => 'Zvolená hodnota pro :attribute není platná.',
+    'file'                 => ':attribute musí být soubor.',
+    'filled'               => ':attribute musí být vyplněno.',
+    'gt'                   => [
+        'numeric' => ':attribute musí být větší než :value.',
+        'file'    => 'Velikost souboru :attribute musí být větší než :value kB.',
+        'string'  => 'Počet znaků :attribute musí být větší :value.',
+        'array'   => 'Pole :attribute musí mít více prvků než :value.',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute musí být větší nebo rovno :value.',
+        'file'    => 'Velikost souboru :attribute musí být větší nebo rovno :value kB.',
+        'string'  => 'Počet znaků :attribute musí být větší nebo rovno :value.',
+        'array'   => 'Pole :attribute musí mít :value prvků nebo více.',
+    ],
+    'image'                => ':attribute musí být obrázek.',
+    'in'                   => 'Zvolená hodnota pro :attribute je neplatná.',
+    'in_array'             => ':attribute není obsažen v :other.',
+    'integer'              => ':attribute musí být celé číslo.',
+    'ip'                   => ':attribute musí být platnou IP adresou.',
+    'ipv4'                 => ':attribute musí být platná IPv4 adresa.',
+    'ipv6'                 => ':attribute musí být platná IPv6 adresa.',
+    'json'                 => ':attribute musí být platný JSON řetězec.',
+    'lt'                   => [
+        'numeric' => ':attribute musí být menší než :value.',
+        'file'    => 'Velikost souboru :attribute musí být menší než :value kB.',
+        'string'  => ':attribute musí obsahovat méně než :value znaků.',
+        'array'   => ':attribute by měl obsahovat méně než :value položek.',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute musí být menší nebo rovno než :value.',
+        'file'    => 'Velikost souboru :attribute musí být menší než :value kB.',
+        'string'  => ':attribute nesmí být delší než :value znaků.',
+        'array'   => ':attribute by měl obsahovat maximálně :value položek.',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute nemůže být větší než :max.',
+        'file'    => 'Velikost souboru :attribute musí být menší než :value kB.',
+        'string'  => ':attribute nemůže být delší než :max znaků.',
+        'array'   => ':attribute nemůže obsahovat více než :max prvků.',
+    ],
+    'mimes'                => ':attribute musí být jeden z následujících datových typů :values.',
+    'mimetypes'            => ':attribute musí být jeden z následujících datových typů :values.',
+    'min'                  => [
+        'numeric' => ':attribute musí být větší než :min.',
+        'file'    => ':attribute musí být větší než :min kB.',
+        'string'  => ':attribute musí být delší než :min znaků.',
+        'array'   => ':attribute musí obsahovat více než :min prvků.',
+    ],
+    'not_in'               => 'Zvolená hodnota pro :attribute je neplatná.',
+    'not_regex'            => ':attribute musí být regulární výraz.',
+    'numeric'              => ':attribute musí být číslo.',
+    'present'              => ':attribute musí být vyplněno.',
+    'regex'                => ':attribute nemá správný formát.',
+    'required'             => ':attribute musí být vyplněno.',
+    'required_if'          => ':attribute musí být vyplněno pokud :other je :value.',
+    'required_unless'      => ':attribute musí být vyplněno dokud :other je v :values.',
+    'required_with'        => ':attribute musí být vyplněno pokud :values je vyplněno.',
+    'required_with_all'    => ':attribute musí být vyplněno pokud :values je zvoleno.',
+    'required_without'     => ':attribute musí být vyplněno pokud :values není vyplněno.',
+    'required_without_all' => ':attribute musí být vyplněno pokud není žádné z :values zvoleno.',
+    'same'                 => ':attribute a :other se musí shodovat.',
+    'size'                 => [
+        'numeric' => ':attribute musí být přesně :size.',
+        'file'    => ':attribute musí mít přesně :size Kilobytů.',
+        'string'  => ':attribute musí být přesně :size znaků dlouhý.',
+        'array'   => ':attribute musí obsahovat právě :size prvků.',
+    ],
+    'starts_with'          => 'The :attribute must start with one of the following: :values',
+    'string'               => ':attribute musí být řetězec znaků.',
+    'timezone'             => ':attribute musí být platná časová zóna.',
+    'unique'               => ':attribute musí být unikátní.',
+    'uploaded'             => 'Nahrávání :attribute se nezdařilo.',
+    'url'                  => 'Formát :attribute je neplatný.',
+    'uuid'                 => ':attribute musí být validní UUID.',
+
+    /*
+    |--------------------------------------------------------------------------
+    | 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' => [
+        'attribute-name' => [
+            'rule-name' => 'custom-message',
+        ],
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    /*
+    |--------------------------------------------------------------------------
+    | 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' => [
+        'password' => 'heslo',
+    ],
+];
index ac2edc621f9256b80eb1c1729b2bf91aafd3bbba..ed880afcf9ab24d0bdd0f30dcf6ae8f5b3de7394 100644 (file)
@@ -11,6 +11,7 @@ return [
     'save' => 'Save',
     'continue' => 'Continue',
     'select' => 'Select',
+    'toggle_all' => 'Toggle All',
     'more' => 'More',
 
     // Form Labels
@@ -23,6 +24,7 @@ return [
     // Actions
     'actions' => 'Actions',
     'view' => 'View',
+    'view_all' => 'View All',
     'create' => 'Create',
     'update' => 'Update',
     'edit' => 'Edit',
@@ -37,6 +39,11 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
 
+    // Sort Options
+    'sort_name' => 'Name',
+    'sort_created_at' => 'Created Date',
+    'sort_updated_at' => 'Updated Date',
+
     // Misc
     'deleted_user' => 'Deleted User',
     'no_activity' => 'No activity to show',
@@ -53,7 +60,11 @@ return [
     'view_profile' => 'View Profile',
     'edit_profile' => 'Edit Profile',
 
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_content' => 'Content',
+
     // Email Content
     'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
     'email_rights' => 'All rights reserved',
-];
\ No newline at end of file
+];
index 2a64f57a3700c113814faa01ba8e6d6cbf9b95dd..abcd2cf2327efb66683f16222ece0fa4b94a5cbe 100644 (file)
@@ -11,6 +11,7 @@ return [
     'recently_updated_pages' => 'Recently Updated Pages',
     'recently_created_chapters' => 'Recently Created Chapters',
     'recently_created_books' => 'Recently Created Books',
+    'recently_created_shelves' => 'Recently Created Shelves',
     'recently_update' => 'Recently Updated',
     'recently_viewed' => 'Recently Viewed',
     'recent_activity' => 'Recent Activity',
@@ -67,11 +68,13 @@ return [
     // Shelves
     'shelf' => 'Shelf',
     'shelves' => 'Shelves',
+    'x_shelves' => ':count Shelf|:count Shelves',
     'shelves_long' => 'Bookshelves',
     'shelves_empty' => 'No shelves have been created',
     'shelves_create' => 'Create New Shelf',
     'shelves_popular' => 'Popular Shelves',
     'shelves_new' => 'New Shelves',
+    'shelves_new_action' => 'New Shelf',
     'shelves_popular_empty' => 'The most popular shelves will appear here.',
     'shelves_new_empty' => 'The most recently created shelves will appear here.',
     'shelves_save' => 'Save Shelf',
@@ -102,6 +105,7 @@ return [
     'books_popular' => 'Popular Books',
     'books_recent' => 'Recent Books',
     'books_new' => 'New Books',
+    'books_new_action' => 'New Book',
     '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',
@@ -117,7 +121,6 @@ return [
     'books_permissions_updated' => 'Book Permissions Updated',
     'books_empty_contents' => 'No pages or chapters have been created for this book.',
     'books_empty_create_page' => 'Create a new page',
-    'books_empty_or' => 'or',
     'books_empty_sort_current_book' => 'Sort the current book',
     'books_empty_add_chapter' => 'Add a chapter',
     'books_permissions_active' => 'Book Permissions Active',
@@ -125,6 +128,11 @@ return [
     'books_navigation' => 'Book Navigation',
     'books_sort' => 'Sort Book Contents',
     'books_sort_named' => 'Sort Book :bookName',
+    'books_sort_name' => 'Sort by Name',
+    'books_sort_created' => 'Sort by Created Date',
+    'books_sort_updated' => 'Sort by Updated Date',
+    'books_sort_chapters_first' => 'Chapters First',
+    'books_sort_chapters_last' => 'Chapters Last',
     'books_sort_show_other' => 'Show Other Books',
     'books_sort_save' => 'Save New Order',
 
@@ -202,6 +210,8 @@ return [
     'pages_revisions_created_by' => 'Created By',
     'pages_revisions_date' => 'Revision Date',
     'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revision #:id',
+    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
     'pages_revisions_changelog' => 'Changelog',
     'pages_revisions_changes' => 'Changes',
     'pages_revisions_current' => 'Current Version',
@@ -267,6 +277,7 @@ 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',
+    'profile_not_created_shelves' => ':userName has not created any shelves',
 
     // Comments
     'comment' => 'Comment',
@@ -288,6 +299,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Are you sure you want to delete this revision?',
+    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
     'revision_delete_success' => 'Revision deleted',
     'revision_cannot_delete_latest' => 'Cannot delete the latest revision.'
 ];
\ No newline at end of file
index ef947829d1cf89cf46ad57c80f83723b30d24f11..e6c24f5a14f57a598d8edd48370981c5ad51b471 100755 (executable)
@@ -12,34 +12,44 @@ return [
     'settings_save_success' => 'Settings saved',
 
     // App Settings
-    'app_settings' => 'App Settings',
-    'app_name' => 'Application name',
-    'app_name_desc' => 'This name is shown in the header and any emails.',
-    'app_name_header' => 'Show Application name in header?',
+    'app_customization' => 'Customization',
+    'app_features_security' => 'Features & Security',
+    'app_name' => 'Application Name',
+    'app_name_desc' => 'This name is shown in the header and in any system-sent emails.',
+    'app_name_header' => 'Show name in header',
+    'app_public_access' => 'Public Access',
+    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
+    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
+    'app_public_access_toggle' => 'Allow public access',
     'app_public_viewing' => 'Allow public viewing?',
-    'app_secure_images' => 'Enable higher security image uploads?',
+    'app_secure_images' => 'Higher Security Image Uploads',
+    'app_secure_images_toggle' => 'Enable higher security image uploads',
     'app_secure_images_desc' => 'For performance reasons, all images are public. This option adds a random, hard-to-guess string in front of image urls. Ensure directory indexes are not enabled to prevent easy access.',
-    'app_editor' => 'Page editor',
+    'app_editor' => 'Page Editor',
     'app_editor_desc' => 'Select which editor will be used by all users to edit pages.',
-    'app_custom_html' => 'Custom HTML head content',
+    'app_custom_html' => 'Custom HTML Head Content',
     'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the <head> section of every page. This is handy for overriding styles or adding analytics code.',
-    'app_logo' => 'Application logo',
+    'app_logo' => 'Application Logo',
     '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' => '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 view to show on the homepage instead of the default view. Page permissions are ignored for selected pages.',
     'app_homepage_select' => 'Select a page',
-    'app_disable_comments' => 'Disable comments',
-    'app_disable_comments_desc' => 'Disable comments across all pages in the application. Existing comments are not shown.',
+    'app_disable_comments' => 'Disable Comments',
+    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
 
     // Registration Settings
-    'reg_settings' => 'Registration Settings',
-    'reg_allow' => 'Allow registration?',
+    'reg_settings' => 'Registration',
+    'reg_enable' => 'Enable Registration',
+    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
     'reg_default_role' => 'Default user role after registration',
-    'reg_confirm_email' => 'Require email confirmation?',
-    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and the below value will be ignored.',
-    'reg_confirm_restrict_domain' => 'Restrict registration to domain',
+    'reg_email_confirmation' => 'Email Confirmation',
+    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
+    'reg_confirm_restrict_domain' => 'Domain Restriction',
     'reg_confirm_restrict_domain_desc' => 'Enter a comma separated list of email domains you would like to restrict registration to. Users will be sent an email to confirm their address before being allowed to interact with the application. <br> Note that users will be able to change their email addresses after successful registration.',
     'reg_confirm_restrict_domain_placeholder' => 'No restriction set',
 
@@ -91,9 +101,16 @@ return [
     'user_profile' => 'User Profile',
     'users_add_new' => 'Add New User',
     'users_search' => 'Search Users',
+    'users_details' => 'User Details',
+    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
+    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
     'users_role' => 'User Roles',
+    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
+    'users_password' => 'User Password',
+    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 5 characters long.',
     'users_external_auth_id' => 'External Authentication ID',
-    'users_password_warning' => 'Only fill the below if you would like to change your password:',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
+    'users_password_warning' => 'Only fill the below if you would like to change your password.',
     'users_system_public' => 'This user represents any guest users that visit your instance. It cannot be used to log in but is assigned automatically.',
     'users_delete' => 'Delete User',
     'users_delete_named' => 'Delete user :userName',
@@ -104,8 +121,9 @@ return [
     'users_edit_profile' => 'Edit Profile',
     'users_edit_success' => 'User successfully updated',
     'users_avatar' => 'User Avatar',
-    'users_avatar_desc' => 'This image should be approx 256px square.',
+    'users_avatar_desc' => 'Select an image to represent this user. This should be approx 256px square.',
     'users_preferred_language' => 'Preferred Language',
+    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
     'users_social_accounts' => 'Social Accounts',
     'users_social_accounts_info' => 'Here you can connect your other accounts for quicker and easier login. Disconnecting an account here does not previously authorized access. Revoke access from your profile settings on the connected social account.',
     'users_social_connect' => 'Connect Account',
@@ -128,6 +146,7 @@ return [
         'nl' => 'Nederlands',
         'pt_BR' => 'Português do Brasil',
         'sk' => 'Slovensky',
+        'cs' => 'Česky',
         'sv' => 'Svenska',
         'kr' => '한국어',
         'ja' => '日本語',
index 3de4c54b001c915a76dffc06225d6809045b0498..9baeb9f3059ae63c8f5f4d19525b838770fe2927 100644 (file)
@@ -50,6 +50,7 @@ return [
         'string'  => 'The :attribute must be at least :min characters.',
         'array'   => 'The :attribute must have at least :min items.',
     ],
+    'no_double_extension'  => 'The :attribute must only have a single file extension.',
     'not_in'               => 'The selected :attribute is invalid.',
     'numeric'              => 'The :attribute must be a number.',
     'regex'                => 'The :attribute format is invalid.',
index 825cff40fefd7d60b6f9f123229132e51ffb97ff..d0caf942033e844053d4e8c39b1ed9b8d564d3bd 100644 (file)
@@ -66,6 +66,7 @@ return [
     'role_cannot_be_edited' => 'Este rol no puede ser editado',
     'role_system_cannot_be_deleted' => 'Este rol es un rol de sistema y no puede ser borrado',
     'role_registration_default_cannot_delete' => 'Este rol no puede ser borrado mientras sea el rol por defecto de registro',
+    'role_cannot_remove_only_admin' => 'Este usuario es el único asignado al rol de administrador. Asigne el rol de administrador a otro usuario antes de intentar eliminarlo.',
 
     // Comments
     'comment_list' => 'Se produjo un error al obtener los comentarios.',
index 3c79de2bb48a94afe5e4c3a9790c35a75874eb78..76bb03f0d3cef3d65d59b0e66274599eb5e39d23 100644 (file)
@@ -74,6 +74,7 @@ return [
     'timezone'             => 'El atributo :attribute debe ser una zona válida.',
     'unique'               => 'El atributo :attribute ya ha sido tomado.',
     'url'                  => 'El atributo :attribute tiene un formato inválid.',
+    'is_image'             => 'El atributo :attribute debe ser una imagen válida.',
 
     /*
     |--------------------------------------------------------------------------
index 4fe15f59fa146cd6a49189a2e4ddac6ce442c565..b722976849f744f48bb9fca0a64f8dfc6369f7f5 100644 (file)
@@ -100,6 +100,38 @@ return [
     'books_sort_show_other' => 'Показать другие книги',
     'books_sort_save' => 'Сохранить новый порядок',
 
+    /**
+     * Shelves
+     */
+    'shelf' => 'Полка',
+    'shelves' => 'Полки',
+    'shelves_long' => 'Книжные полки',
+    'shelves_empty' => 'Полки не созданы',
+    'shelves_create' => 'Создать новую полку',
+    'shelves_popular' => 'Популярные полки',
+    'shelves_new' => 'Новые полки',
+    'shelves_popular_empty' => 'Популярные полки появятся здесь.',
+    'shelves_new_empty' => 'Последние созданные полки появятся здесь.',
+    'shelves_save' => 'Сохранить полку',
+    'shelves_books' => 'Книги из этой полки',
+    'shelves_add_books' => 'Добавить книгу в эту полку',
+    'shelves_drag_books' => 'Перетащите книгу сюда, чтобы добавить на эту полку',
+    'shelves_empty_contents' => 'На этой полке нет книг',
+    'shelves_edit_and_assign' => 'Изменить полку для привязки книг',
+    'shelves_edit_named' => 'Редактировать полку :name',
+    'shelves_edit' => 'Редактировать книжную полку',
+    'shelves_delete' => 'Удалить книжную полку',
+    'shelves_delete_named' => 'Удалить книжную полку :name',
+    'shelves_delete_explain' => "Это приведет к удалению полки с именем ':name'. Привязанные книги удалены не будут.",
+    'shelves_delete_confirmation' => 'Вы уверены, что хотите удалить эту полку?',
+    'shelves_permissions' => 'Доступы к книжной полке',
+    'shelves_permissions_updated' => 'Доступы к книжной полке обновлены',
+    'shelves_permissions_active' => 'Доступы к книжной полке активны',
+    'shelves_copy_permissions_to_books' => 'Наследовать доступы книгам',
+    'shelves_copy_permissions' => 'Копировать доступы',
+    'shelves_copy_permissions_explain' => 'Это применит текущие настройки доступов этой книжной полки ко всем книгам, содержащимся внутри. Перед активацией убедитесь, что все изменения в доступах этой книжной полки сохранены.',
+    'shelves_copy_permission_success' => 'Доступы книжной полки скопированы для :count books',
+
     /**
      * Chapters
      */
@@ -206,6 +238,7 @@ return [
     'page_tags' => 'Теги страницы',
     'chapter_tags' => 'Теги главы',
     'book_tags' => 'Теги книги',
+    'shelf_tags' => 'Теги полки',
     'tag' => 'Тег',
     'tags' =>  'Теги',
     'tag_value' => 'Значение тега (опционально)',
index b52b5f13e9148a497c79248f4e811885da5c7092..ecdda652027285604225e1d0c58e515950d7622e 100644 (file)
@@ -1,12 +1,12 @@
 <div class="form-group">
     <label for="username">{{ trans('auth.username') }}</label>
-    @include('form/text', ['name' => 'username', 'tabindex' => 1])
+    @include('form.text', ['name' => 'username', 'tabindex' => 1])
 </div>
 
 @if(session('request-email', false) === true)
     <div class="form-group">
         <label for="email">{{ trans('auth.email') }}</label>
-        @include('form/text', ['name' => 'email', 'tabindex' => 1])
+        @include('form.text', ['name' => 'email', 'tabindex' => 1])
         <span class="text-neg">
             {{ trans('auth.ldap_email_hint') }}
         </span>
@@ -15,5 +15,5 @@
 
 <div class="form-group">
     <label for="password">{{ trans('auth.password') }}</label>
-    @include('form/password', ['name' => 'password', 'tabindex' => 2])
+    @include('form.password', ['name' => 'password', 'tabindex' => 2])
 </div>
\ No newline at end of file
index 4ea1f35ba2b89c4ff8b34e8839cca16943feb16c..a12fbd7531ce39d4b16f202651ddb7139e4f2a3c 100644 (file)
@@ -1,10 +1,10 @@
 <div class="form-group">
     <label for="email">{{ trans('auth.email') }}</label>
-    @include('form/text', ['name' => 'email', 'tabindex' => 1])
+    @include('form.text', ['name' => 'email', 'tabindex' => 1])
 </div>
 
 <div class="form-group">
     <label for="password">{{ trans('auth.password') }}</label>
-    @include('form/password', ['name' => 'password', 'tabindex' => 2])
-    <span class="block small"><a href="{{ baseUrl('/password/email') }}">{{ trans('auth.forgot_password') }}</a></span>
+    @include('form.password', ['name' => 'password', 'tabindex' => 1])
+    <span class="block small mt-s"><a href="{{ baseUrl('/password/email') }}">{{ trans('auth.forgot_password') }}</a></span>
 </div>
\ No newline at end of file
index f3847bb043a1f5312583f813dab049d4a807ed85..eb7ca2da8751a852131e076133095f3a14a120c8 100644 (file)
@@ -1,44 +1,48 @@
-@extends('public')
-
-@section('header-buttons')
-    @if(setting('registration-enabled', false))
-        <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
-    @endif
-@stop
+@extends('simple-layout')
 
 @section('content')
 
-    <div class="text-center">
-        <div class="card center-box">
-            <h3>@icon('login') {{ title_case(trans('auth.log_in')) }}</h3>
+    <div class="container very-small">
 
-            <div class="body">
-                <form action="{{ baseUrl("/login") }}" method="POST" id="login-form">
-                    {!! csrf_field() !!}
+        <div class="my-l">&nbsp;</div>
 
-                    @include('auth/forms/login/' . $authMethod)
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ title_case(trans('auth.log_in')) }}</h1>
 
-                    <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>
+            <form action="{{ baseUrl("/login") }}" method="POST" id="login-form" class="mt-l">
+                {!! csrf_field() !!}
+
+                <div class="stretch-inputs">
+                    @include('auth.forms.login.' . $authMethod)
+                </div>
 
-                    <div class="from-group">
-                        <button class="button block pos" tabindex="3">@icon('login') {{ title_case(trans('auth.log_in')) }}</button>
+                <div class="grid half collapse-xs gap-xl v-center">
+                    <div class="text-left ml-xxs">
+                        @include('components.custom-checkbox', [
+                            'name' => 'remember',
+                            'checked' => false,
+                            'value' => 'on',
+                            'label' => trans('auth.remember_me'),
+                        ])
                     </div>
-                </form>
+                    <div class="text-right">
+                        <button class="button primary" tabindex="3">{{ title_case(trans('auth.log_in')) }}</button>
+                    </div>
+                </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) }}">
+            @if(count($socialDrivers) > 0)
+                <hr class="my-l">
+                @foreach($socialDrivers as $driver => $name)
+                    <div>
+                        <a id="social-login-{{$driver}}" class="button outline block svg" href="{{ baseUrl("/login/service/" . $driver) }}">
                             @icon('auth/' . $driver)
                             {{ trans('auth.log_in_with', ['socialDriver' => $name]) }}
                         </a>
-                    @endforeach
-                @endif
-            </div>
+                    </div>
+                @endforeach
+            @endif
         </div>
     </div>
 
index 38f5cc07a9d4b14d2f0ffc15f02250bf244cd413..de4edff0a8e479da8999a802727d7d76c6f1cb72 100644 (file)
@@ -1,37 +1,25 @@
-@extends('public')
-
-@section('header-buttons')
-    <a href="{{ baseUrl("/login") }}">@icon('login') {{ trans('auth.log_in') }}</a>
-    @if(setting('registration-enabled'))
-        <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
-    @endif
-@stop
+@extends('simple-layout')
 
 @section('content')
+    <div class="container very-small mt-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('auth.reset_password') }}</h1>
 
+            <p class="text-muted small">{{ trans('auth.reset_password_send_instructions') }}</p>
 
-    <div class="text-center">
-        <div class="card center-box">
-            <h3>@icon('permission') {{ trans('auth.reset_password') }}</h3>
-
-            <div class="body">
-                <p class="muted small">{{ trans('auth.reset_password_send_instructions') }}</p>
+            <form action="{{ baseUrl("/password/email") }}" method="POST" class="stretch-inputs">
+                {!! csrf_field() !!}
 
-                <form action="{{ baseUrl("/password/email") }}" method="POST">
-                    {!! csrf_field() !!}
+                <div class="form-group">
+                    <label for="email">{{ trans('auth.email') }}</label>
+                    @include('form.text', ['name' => 'email'])
+                </div>
 
-                    <div class="form-group">
-                        <label for="email">{{ trans('auth.email') }}</label>
-                        @include('form/text', ['name' => 'email'])
-                    </div>
-
-                    <div class="from-group text-right">
-                        <button class="button primary">{{ trans('auth.reset_password_send_button') }}</button>
-                    </div>
-                </form>
-            </div>
+                <div class="from-group text-right mt-m">
+                    <button class="button primary">{{ trans('auth.reset_password_send_button') }}</button>
+                </div>
+            </form>
 
         </div>
     </div>
-
 @stop
\ No newline at end of file
index 0f66bd47360ce63ee106708ef78397cddd0b0015..fa6ad5b9a327b6cd0945709f27c31616a21913f8 100644 (file)
@@ -1,43 +1,34 @@
-@extends('public')
-
-@section('header-buttons')
-    <a href="{{ baseUrl("/login") }}">@icon('login') {{ trans('auth.log_in') }}</a>
-    @if(setting('registration-enabled'))
-        <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
-    @endif
-@stop
+@extends('simple-layout')
 
 @section('content')
 
-    <div class="text-center">
-        <div class="card center-box">
-            <h3>@icon('permission') {{ trans('auth.reset_password') }}</h3>
-
-            <div class="body">
-                <form action="{{ baseUrl("/password/reset") }}" method="POST">
-                    {!! csrf_field() !!}
-                    <input type="hidden" name="token" value="{{ $token }}">
-
-                    <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'])
-                    </div>
-
-                    <div class="form-group">
-                        <label for="password_confirmation">{{ trans('auth.password_confirm') }}</label>
-                        @include('form/password', ['name' => 'password_confirmation'])
-                    </div>
-
-                    <div class="from-group text-right">
-                        <button class="button primary">{{ trans('auth.reset_password') }}</button>
-                    </div>
-                </form>
-            </div>
+    <div class="container very-small mt-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('auth.reset_password') }}</h1>
+
+            <form action="{{ baseUrl("/password/reset") }}" method="POST" class="stretch-inputs">
+                {!! csrf_field() !!}
+                <input type="hidden" name="token" value="{{ $token }}">
+
+                <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'])
+                </div>
+
+                <div class="form-group">
+                    <label for="password_confirmation">{{ trans('auth.password_confirm') }}</label>
+                    @include('form.password', ['name' => 'password_confirmation'])
+                </div>
+
+                <div class="from-group text-right mt-m">
+                    <button class="button primary">{{ trans('auth.reset_password') }}</button>
+                </div>
+            </form>
 
         </div>
     </div>
index 22d0f4acbd9732f429e6e97ebc49a1227a3778c2..8fbf8abbbd7beae0c6d70af0a87d3183c4a57492 100644 (file)
@@ -1,19 +1,11 @@
-@extends('public')
-
-@section('header-buttons')
-    @if(!$signedIn)
-        <a href="{{ baseUrl("/login") }}">@icon('login') {{ trans('auth.log_in') }}</a>
-    @endif
-@stop
+@extends('simple-layout')
 
 @section('content')
 
-    <div class="text-center">
-        <div class="card center-box">
-            <h3>@icon('users') {{ trans('auth.register_thanks') }}</h3>
-            <div class="body">
-                <p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
-            </div>
+    <div class="container very-small mt-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('auth.register_thanks') }}</h1>
+            <p>{{ trans('auth.register_confirm', ['appName' => setting('app-name')]) }}</p>
         </div>
     </div>
 
index 900b394e3bf3cc61e3eeff3de6e7ee61d4788ac6..d1e69e0a0706cf99f138b9f3b0a3fb38075f9d22 100644 (file)
@@ -1,50 +1,54 @@
-@extends('public')
-
-@section('header-buttons')
-    <a href="{{ baseUrl("/login") }}">@icon('login') {{ trans('auth.log_in') }}</a>
-@stop
+@extends('simple-layout')
 
 @section('content')
+    <div class="container very-small">
 
-    <div class="text-center">
-        <div class="card center-box">
-            <h3>@icon('new-user')  {{ title_case(trans('auth.sign_up')) }}</h3>
-            <div class="body">
-                <form action="{{ baseUrl("/register") }}" method="POST">
-                    {!! csrf_field() !!}
+        <div class="my-l">&nbsp;</div>
 
-                    <div class="form-group">
-                        <label for="email">{{ trans('auth.name') }}</label>
-                        @include('form/text', ['name' => 'name'])
-                    </div>
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ title_case(trans('auth.sign_up')) }}</h1>
 
-                    <div class="form-group">
-                        <label for="email">{{ trans('auth.email') }}</label>
-                        @include('form/text', ['name' => 'email'])
-                    </div>
+            <form action="{{ baseUrl("/register") }}" method="POST" class="mt-l stretch-inputs">
+                {!! csrf_field() !!}
 
-                    <div class="form-group">
-                        <label for="password">{{ trans('auth.password') }}</label>
-                        @include('form/password', ['name' => 'password', 'placeholder' => trans('auth.password_hint')])
-                    </div>
+                <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 class="grid half collapse-xs gap-xl v-center mt-m">
+                    <div class="text-small">
+                        <a href="{{ baseUrl('/login') }}">Already have an account?</a>
                     </div>
-                </form>
+                    <div class="from-group text-right">
+                        <button class="button primary">{{ trans('auth.create_account') }}</button>
+                    </div>
+                </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) }}">
+            @if(count($socialDrivers) > 0)
+                <hr class="my-l">
+                @foreach($socialDrivers as $driver => $name)
+                    <div>
+                        <a id="social-register-{{$driver}}" class="button block outline svg" href="{{ baseUrl("/register/service/" . $driver) }}">
                             @icon('auth/' . $driver)
                             {{ trans('auth.sign_up_with', ['socialDriver' => $name]) }}
                         </a>
-                    @endforeach
-                @endif
-            </div>
+                    </div>
+                @endforeach
+            @endif
         </div>
     </div>
-
-
 @stop
index b02d45d22e97f24c011e7ac891501cebb8c96d54..54bf6eda3314ac73d4b07b3de18d645e1f40dfe1 100644 (file)
@@ -1,34 +1,34 @@
-@extends('public')
+@extends('simple-layout')
 
 @section('content')
 
-    <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('users') {{ 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 class="container very-small mt-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('auth.email_not_confirmed') }}</h1>
+
+            <p>{{ trans('auth.email_not_confirmed_text') }}<br>
+                {{ trans('auth.email_not_confirmed_click_link') }}
+            </p>
+            <p>
+                {{ trans('auth.email_not_confirmed_resend') }}
+            </p>
 
+            <form action="{{ baseUrl("/register/confirm/resend") }}" method="POST" class="stretch-inputs">
+                {!! 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 text-right mt-m">
+                    <button type="submit" class="button primary">{{ trans('auth.email_not_confirmed_resend_button') }}</button>
+                </div>
+            </form>
+
+        </div>
     </div>
 
 @stop
index fdd248091d9dfcf29ddd3d819f27c75daa63d597..367a2cd8b9ba69536d985dc85cbf1a14681c7395 100644 (file)
     <script src="{{ baseUrl('/translations') }}"></script>
 
     @yield('head')
-
-    @include('partials/custom-styles')
-
+    @include('partials.custom-styles')
     @include('partials.custom-head')
-</head>
-<body class="@yield('body-class')" ng-app="bookStack">
-
-    @include('partials/notifications')
 
-    <header id="header">
-        <div class="container fluid">
-            <div class="row">
-                <div class="col-sm-4 col-md-3">
-                    <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
-                        @if (setting('app-name-header'))
-                            <span class="logo-text">{{ setting('app-name') }}</span>
-                        @endif
-                    </a>
-                </div>
-                <div class="col-sm-8 col-md-9">
-                    <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">@icon('search') </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">
-                            @if(userCanOnAny('view', \BookStack\Entities\Bookshelf::class) || userCan('bookshelf-view-own'))
-                                <a href="{{ baseUrl('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
-                            @endif
-                            <a href="{{ baseUrl('/books') }}">@icon('book'){{ trans('entities.books') }}</a>
-                            @if(signedInUser() && userCan('settings-manage'))
-                                <a href="{{ baseUrl('/settings') }}">@icon('settings'){{ trans('settings.settings') }}</a>
-                            @endif
-                            @if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
-                                <a href="{{ baseUrl('/settings/users') }}">@icon('users'){{ trans('settings.users') }}</a>
-                            @endif
-                            @if(!signedInUser())
-                                @if(setting('registration-enabled', false))
-                                    <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
-                                @endif
-                                <a href="{{ baseUrl('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
-                            @endif
-                        </div>
-                        @if(signedInUser())
-                            @include('partials._header-dropdown', ['currentUser' => user()])
-                        @endif
+    @stack('head')
+</head>
+<body class="@yield('body-class')">
 
-                    </div>
-                </div>
-            </div>
-        </div>
-    </header>
+    @include('partials.notifications')
+    @include('common.header')
 
     <section id="content" class="block">
         @yield('content')
     </section>
 
-    <div back-to-top>
+    <div back-to-top class="primary-background">
         <div class="inner">
             @icon('chevron-up') <span>{{ trans('common.back_to_top') }}</span>
         </div>
     </div>
-@yield('bottom')
-<script src="{{ versioned_asset('dist/app.js') }}"></script>
-@yield('scripts')
+
+    @yield('bottom')
+    <script src="{{ versioned_asset('dist/app.js') }}"></script>
+    @yield('scripts')
+
 </body>
 </html>
index 2d5e6c4558ebfd37332316774777bcbfdfbea9db..882ce556a177525cebd9ceb7c30ccf7f3f48ed20 100644 (file)
@@ -1,28 +1,37 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-8 faded">
-        <div class="breadcrumbs">
-            <a href="{{ baseUrl('/books') }}" class="text-button">@icon('book'){{ trans('entities.books') }}</a>
-            <span class="sep">&raquo;</span>
-            <a href="{{ baseUrl('/create-book') }}" class="text-button">@icon('add'){{ trans('entities.books_create') }}</a>
-        </div>
-    </div>
-@stop
-
 @section('body')
+    <div class="container small">
+        <div class="my-s">
+            @if (isset($bookshelf))
+                @include('partials.breadcrumbs', ['crumbs' => [
+                    $bookshelf,
+                    $bookshelf->getUrl('/create-book') => [
+                        'text' => trans('entities.books_create'),
+                        'icon' => 'add'
+                    ]
+                ]])
+            @else
+                @include('partials.breadcrumbs', ['crumbs' => [
+                    '/books' => [
+                        'text' => trans('entities.books'),
+                        'icon' => 'book'
+                    ],
+                    '/create-book' => [
+                        'text' => trans('entities.books_create'),
+                        'icon' => 'add'
+                    ]
+                ]])
+            @endif
+        </div>
 
-<div class="container small">
-    <p>&nbsp;</p>
-    <div class="card">
-        <h3>@icon('add') {{ trans('entities.books_create') }}</h3>
-        <div class="body">
-            <form action="{{ baseUrl("/books") }}" method="POST" enctype="multipart/form-data">
-                @include('books/form')
+        <div class="content-wrap card">
+            <h1 class="list-heading">{{ trans('entities.books_create') }}</h1>
+            <form action="{{ isset($bookshelf) ? $bookshelf->getUrl('/create-book') : baseUrl('/books') }}" method="POST" enctype="multipart/form-data">
+                @include('books.form')
             </form>
         </div>
     </div>
-</div>
-<p class="margin-top large"><br></p>
+
     @include('components.image-manager', ['imageType' => 'cover'])
 @stop
\ No newline at end of file
index 0ac98e8956708babedacff60b39abf207e9aa3c8..2860e8bcdf15a63dbb2dec9cee399c4f39020ea0 100644 (file)
@@ -1,28 +1,30 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('books._breadcrumbs', ['book' => $book])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ 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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $book->getUrl('/delete') => [
+                    'text' => trans('entities.books_delete'),
+                    'icon' => 'delete',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('entities.books_delete') }}</h1>
+            <p>{{ trans('entities.books_delete_explain', ['bookName' => $book->name]) }}</p>
+            <p class="text-neg"><strong>{{ trans('entities.books_delete_confirmation') }}</strong></p>
+
+            <form action="{{$book->getUrl()}}" method="POST" class="text-right">
+                {!! 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 primary">{{ trans('common.confirm') }}</button>
+            </form>
         </div>
 
     </div>
index cb1ffc461035eab044f670583b196a0dbe4dbfbb..f048b543b63adff54b5100345f57d5f3bc8e0a78 100644 (file)
@@ -1,24 +1,27 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('books._breadcrumbs', ['book' => $book])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('edit') {{ 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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $book->getUrl('/edit') => [
+                    'text' => trans('entities.books_edit'),
+                    'icon' => 'edit',
+                ]
+            ]])
+        </div>
+
+        <div class="content-wrap card">
+            <h1 class="list-heading">{{ trans('entities.books_edit') }}</h1>
+            <form action="{{ $book->getUrl() }}" method="POST">
+                <input type="hidden" name="_method" value="PUT">
+                @include('books.form', ['model' => $book])
+            </form>
         </div>
     </div>
-@include('components.image-manager', ['imageType' => 'cover'])
+
+    @include('components.image-manager', ['imageType' => 'cover'])
 @stop
\ No newline at end of file
index 18440a74da82b8ecfc47e78f3e806cb8369f61ab..e1fabd800bd1c42c6cd42257bb6e81e2a28fc23c 100644 (file)
     @include('partials.custom-head')
 </head>
 <body>
-<div class="container">
-    <div class="row">
-        <div class="col-md-8 col-md-offset-2">
-            <div class="page-content">
 
-                <h1 style="font-size: 4.8em">{{$book->name}}</h1>
+<div class="page-content">
 
-                <p>{{ $book->description }}</p>
+    <h1 style="font-size: 4.8em">{{$book->name}}</h1>
 
-                @if(count($bookChildren) > 0)
-                <ul class="contents">
-                    @foreach($bookChildren as $bookChild)
-                        <li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
-                        @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                            <ul>
-                                @foreach($bookChild->pages as $page)
-                                    <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
-                                @endforeach
-                            </ul>
-                        @endif
-                    @endforeach
-                </ul>
+    <p>{{ $book->description }}</p>
+
+    @if(count($bookChildren) > 0)
+        <ul class="contents">
+            @foreach($bookChildren as $bookChild)
+                <li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
+                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                    <ul>
+                        @foreach($bookChild->pages as $page)
+                            <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
+                        @endforeach
+                    </ul>
                 @endif
+            @endforeach
+        </ul>
+    @endif
+
+    @foreach($bookChildren as $bookChild)
+        <div class="page-break"></div>
+        <h1 id="{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</h1>
 
-                @foreach($bookChildren as $bookChild)
+        @if($bookChild->isA('chapter'))
+            <p>{{ $bookChild->description }}</p>
+
+            @if(count($bookChild->pages) > 0)
+                @foreach($bookChild->pages as $page)
                     <div class="page-break"></div>
-                    <h1 id="{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</h1>
-                    @if($bookChild->isA('chapter'))
-                        <p>{{ $bookChild->description }}</p>
-                        @if(count($bookChild->pages) > 0)
-                            @foreach($bookChild->pages as $page)
-                                <div class="page-break"></div>
-                                <div class="chapter-hint">{{$bookChild->name}}</div>
-                                <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
-                                {!! $page->html !!}
-                            @endforeach
-                        @endif
-                    @else
-                        {!! $bookChild->html !!}
-                    @endif
+                    <div class="chapter-hint">{{$bookChild->name}}</div>
+                    <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+                    {!! $page->html !!}
                 @endforeach
+            @endif
+
+        @else
+            {!! $bookChild->html !!}
+        @endif
+
+    @endforeach
 
-            </div>
-        </div>
-    </div>
 </div>
+
 </body>
 </html>
index bf94b5b076ed189a3dc723ad6ce5013522daf049..ebbc279fdb515d85dab7f88ed736a71498ca8128 100644 (file)
@@ -2,12 +2,12 @@
 {{ csrf_field() }}
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form/text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name'])
 </div>
 
 <div class="form-group description-input">
     <label for="description">{{ trans('common.description') }}</label>
-    @include('form/textarea', ['name' => 'description'])
+    @include('form.textarea', ['name' => 'description'])
 </div>
 
 <div class="form-group" collapsible id="logo-control">
@@ -41,5 +41,5 @@
 
 <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>
+    <button type="submit" class="button primary">{{ trans('entities.books_save') }}</button>
 </div>
\ No newline at end of file
index 9bbf691006822cdd4935dc6c9f46c3b93c850e5c..e1d37753a12afef67aeb527fff0d23c809cdb176 100644 (file)
@@ -1,18 +1,19 @@
-<div class="book-grid-item grid-card"  data-entity-type="book" data-entity-id="{{$book->id}}">
-    <div class="featured-image-container">
-        <a href="{{$book->getUrl()}}" title="{{$book->name}}">
-            <img src="{{$book->getBookCover()}}" alt="{{$book->name}}">
-        </a>
+<a href="{{$book->getUrl()}}" class="grid-card"  data-entity-type="book" data-entity-id="{{$book->id}}">
+    <div class="bg-book featured-image-container-wrap">
+        <div class="featured-image-container" @if($book->cover) style="background-image: url('{{ $book->getBookCover() }}')"@endif>
+        </div>
+        @icon('book')
     </div>
     <div class="grid-card-content">
-        <h2><a class="break-text" href="{{$book->getUrl()}}" title="{{$book->name}}">{{$book->getShortName(35)}}</a></h2>
+        <h2>{{$book->getShortName(35)}}</h2>
         @if(isset($book->searchSnippet))
-            <p >{!! $book->searchSnippet !!}</p>
+            <p class="text-muted">{!! $book->searchSnippet !!}</p>
         @else
-            <p >{{ $book->getExcerpt(130) }}</p>
+            <p class="text-muted">{{ $book->getExcerpt(130) }}</p>
         @endif
     </div>
-    <div class="grid-card-footer text-muted text-small">
-        <span>@include('partials.entity-meta', ['entity' => $book])</span>
+    <div class="grid-card-footer text-muted ">
+        <p>@icon('star')<span title="{{$book->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $book->created_at->diffForHumans()]) }}</span></p>
+        <p>@icon('edit')<span title="{{ $book->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $book->updated_at->diffForHumans()]) }}</span></p>
     </div>
-</div>
\ No newline at end of file
+</a>
\ No newline at end of file
index 84150203f081e2345f466a0f0b3399da3acb2298..38286c2016983e8bd5033cf1c1b62af42762414e 100644 (file)
@@ -1,47 +1,52 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-xs-6">
-        <div class="action-buttons text-left">
-            @include('books/view-toggle', ['booksViewType' => $booksViewType])
-        </div>
-    </div>
-    <div class="col-xs-6 faded">
-        <div class="action-buttons">
-            @if($currentUser->can('book-create-all'))
-                <a href="{{ baseUrl("/create-book") }}" class="text-pos text-button">@icon('add'){{ trans('entities.books_create') }}</a>
-            @endif
-        </div>
-    </div>
+@section('container-classes', 'mt-xl')
+
+@section('body')
+    @include('books.list', ['books' => $books, 'view' => $view])
 @stop
 
-@section('sidebar')
+@section('left')
     @if($recents)
-        <div id="recents" class="card">
-            <h3>@icon('view') {{ trans('entities.recently_viewed') }}</h3>
-            @include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
+        <div id="recents" class="mb-xl">
+            <h5>{{ trans('entities.recently_viewed') }}</h5>
+            @include('partials.entity-list', ['entities' => $recents, 'style' => 'compact'])
         </div>
     @endif
 
-    <div id="popular" class="card">
-        <h3>@icon('popular') {{ trans('entities.books_popular') }}</h3>
+    <div id="popular" class="mb-xl">
+        <h5>{{ trans('entities.books_popular') }}</h5>
         @if(count($popular) > 0)
-            @include('partials/entity-list', ['entities' => $popular, 'style' => 'compact'])
+            @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>@icon('star-circle') {{ trans('entities.books_new') }}</h3>
+    <div id="new" class="mb-xl">
+        <h5>{{ trans('entities.books_new') }}</h5>
         @if(count($popular) > 0)
-            @include('partials/entity-list', ['entities' => $new, 'style' => 'compact'])
+            @include('partials.entity-list', ['entities' => $new, 'style' => 'compact'])
         @else
             <div class="body text-muted">{{ trans('entities.books_new_empty') }}</div>
         @endif
     </div>
 @stop
 
-@section('body')
-    @include('books/list', ['books' => $books, 'bookViewType' => $booksViewType])
+@section('right')
+
+    <div class="actions mb-xl">
+        <h5>{{ trans('common.actions') }}</h5>
+        <div class="icon-list text-primary">
+            @if($currentUser->can('book-create-all'))
+                <a href="{{ baseUrl("/create-book") }}" class="icon-list-item">
+                    <span>@icon('add')</span>
+                    <span>{{ trans('entities.books_create') }}</span>
+                </a>
+            @endif
+
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'book'])
+        </div>
+    </div>
+
 @stop
\ No newline at end of file
index 05d7e90ef73a9846a8ba7bb48ddc6b50bc6cf951..17cf4c71f9878117471b6afef38f3eccff615331 100644 (file)
@@ -1,10 +1,11 @@
-<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()}}">@icon('book')<span class="entity-list-item-name break-text">{{$book->name}}</span></a></h4>
-    <div class="entity-item-snippet">
-        @if(isset($book->searchSnippet))
-            <p class="text-muted break-text">{!! $book->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $book->getExcerpt() }}</p>
-        @endif
+<a href="{{ $book->getUrl() }}" class="book entity-list-item" data-entity-type="book" data-entity-id="{{$book->id}}">
+    <div class="entity-list-item-image bg-book" style="background-image: url('{{ $book->getBookCover() }}')">
+        @icon('book')
     </div>
-</div>
\ No newline at end of file
+    <div class="content">
+        <h4 class="entity-list-item-name break-text">{{ $book->name }}</h4>
+        <div class="entity-item-snippet">
+            <p class="text-muted break-text mb-s">{{ $book->getExcerpt() }}</p>
+        </div>
+    </div>
+</a>
\ No newline at end of file
index 9459cc0088b9864c6badfa5a44246565b47e84ab..91a2c716e275bd1cefe9dd1bc6fd7a6b3c86191d 100644 (file)
@@ -1,23 +1,30 @@
 
-<div class="container{{ $booksViewType === 'list' ? ' small' : '' }}">
-    <h1>{{ trans('entities.books') }}</h1>
+<div class="content-wrap mt-m card">
+    <div class="grid half v-center">
+        <h1 class="list-heading">{{ trans('entities.books') }}</h1>
+        <div class="text-right">
+
+            @include('partials.sort', ['options' => $sortOptions, 'order' => $order, 'sort' => $sort, 'type' => 'books'])
+
+        </div>
+    </div>
     @if(count($books) > 0)
-        @if($booksViewType === 'list')
-            @foreach($books as $book)
-                @include('books/list-item', ['book' => $book])
-                <hr>
-            @endforeach
-            {!! $books->render() !!}
+        @if($view === 'list')
+            <div class="entity-list">
+                @foreach($books as $book)
+                    @include('books.list-item', ['book' => $book])
+                @endforeach
+            </div>
         @else
              <div class="grid third">
                 @foreach($books as $key => $book)
-                        @include('books/grid-item', ['book' => $book])
+                    @include('books.grid-item', ['book' => $book])
                 @endforeach
              </div>
-            <div>
-                {!! $books->render() !!}
-            </div>
         @endif
+        <div>
+            {!! $books->render() !!}
+        </div>
     @else
         <p class="text-muted">{{ trans('entities.books_empty') }}</p>
         @if(userCan('books-create-all'))
diff --git a/resources/views/books/permissions.blade.php b/resources/views/books/permissions.blade.php
new file mode 100644 (file)
index 0000000..64322cf
--- /dev/null
@@ -0,0 +1,23 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container">
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $book->getUrl('/permissions') => [
+                    'text' => trans('entities.books_permissions'),
+                    'icon' => 'lock',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.books_permissions') }}</h1>
+            @include('form.entity-permissions', ['model' => $book])
+        </div>
+    </div>
+
+@stop
diff --git a/resources/views/books/restrictions.blade.php b/resources/views/books/restrictions.blade.php
deleted file mode 100644 (file)
index 2a6eb0b..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-@extends('simple-layout')
-
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('books._breadcrumbs', ['book' => $book])
-    </div>
-@stop
-
-@section('body')
-
-    <div class="container">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('lock') {{ trans('entities.books_permissions') }}</h3>
-            <div class="body">
-                @include('form/restriction-form', ['model' => $book])
-            </div>
-        </div>
-    </div>
-
-@stop
index e5845b4956ba0b9db8a3349dd613f5342f8d83e8..3141f5ab29c410cd88b699f2dc1d542bd46c9e4f 100644 (file)
@@ -1,67 +1,68 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-sm-6 col-xs-1  faded">
-        @include('books._breadcrumbs', ['book' => $book])
+@section('container-attrs')
+    id="entity-dashboard"
+    entity-id="{{ $book->id }}"
+    entity-type="book"
+@stop
+
+@section('body')
+
+    <div class="mb-s">
+        @include('partials.breadcrumbs', ['crumbs' => [
+            $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">@icon('export'){{ 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('/create-page') }}" class="text-pos text-button">@icon('add'){{ trans('entities.pages_new') }}</a>
-            @endif
-            @if(userCan('chapter-create', $book))
-                <a href="{{ $book->getUrl('/create-chapter') }}" class="text-pos text-button">@icon('add'){{ 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">@icon('more'){{ trans('common.more') }}</a>
-                    <ul>
-                        @if(userCan('book-update', $book))
-                            <li><a href="{{ $book->getUrl('/edit') }}" class="text-primary">@icon('edit'){{ trans('common.edit') }}</a></li>
-                            <li><a href="{{ $book->getUrl('/sort') }}" class="text-primary">@icon('sort'){{ trans('common.sort') }}</a></li>
+
+    <div class="content-wrap card">
+        <h1 class="break-text" v-pre>{{$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="entity-list book-contents" v-pre>
+                    @foreach($bookChildren as $childElement)
+                        @if($childElement->isA('chapter'))
+                            @include('chapters.list-item', ['chapter' => $childElement])
+                        @else
+                            @include('pages.list-item', ['page' => $childElement])
                         @endif
-                        @if(userCan('restrictions-manage', $book))
-                            <li><a href="{{ $book->getUrl('/permissions') }}" class="text-primary">@icon('lock'){{ trans('entities.permissions') }}</a></li>
+                    @endforeach
+                </div>
+            @else
+                <div class="mt-xl" v-pre>
+                    <hr>
+                    <p class="text-muted italic mb-m mt-xl">{{ trans('entities.books_empty_contents') }}</p>
+
+                    <div class="icon-list block inline">
+                        @if(userCan('page-create', $book))
+                            <a href="{{ $book->getUrl('/create-page') }}" class="icon-list-item text-page">
+                                <span class="icon">@icon('page')</span>
+                                <span>{{ trans('entities.books_empty_create_page') }}</span>
+                            </a>
                         @endif
-                        @if(userCan('book-delete', $book))
-                            <li><a href="{{ $book->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('common.delete') }}</a></li>
+                        @if(userCan('chapter-create', $book))
+                            <a href="{{ $book->getUrl('/create-chapter') }}" class="icon-list-item text-chapter">
+                                <span class="icon">@icon('chapter')</span>
+                                <span>{{ trans('entities.books_empty_add_chapter') }}</span>
+                            </a>
                         @endif
-                    </ul>
+                    </div>
+
                 </div>
             @endif
         </div>
+
+        @include('partials.entity-dashboard-search-results')
     </div>
-@stop
 
-@section('sidebar')
+@stop
 
-    @if($book->tags->count() > 0)
-        <section>
-            @include('components.tag-list', ['entity' => $book])
-        </section>
-    @endif
 
-    <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">@icon('search')</button>
-                <button v-if="searching" v-cloak class="text-neg" v-on:click="clearSearch()" type="button">@icon('close')</button>
-            </form>
-        </div>
-    </div>
+@section('right')
 
-    <div class="card entity-details">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
-        <div class="body text-small text-muted blended-links">
+    <div class="mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
+        <div class="text-small text-muted blended-links">
             @include('partials.entity-meta', ['entity' => $book])
             @if($book->restricted)
                 <div class="active-restriction">
         </div>
     </div>
 
-    @if(count($activity) > 0)
-        <div class="activity card">
-            <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
-            @include('partials/activity-list', ['activity' => $activity])
-        </div>
-    @endif
-@stop
 
-@section('container-attrs')
-    id="entity-dashboard"
-    entity-id="{{ $book->id }}"
-    entity-type="book"
-@stop
+    <div class="actions mb-xl">
+        <h5>{{ trans('common.actions') }}</h5>
+        <div class="icon-list text-primary">
 
-@section('body')
+            @if(userCan('page-create', $book))
+                <a href="{{ $book->getUrl('/create-page') }}" class="icon-list-item">
+                    <span>@icon('add')</span>
+                    <span>{{ trans('entities.pages_new') }}</span>
+                </a>
+            @endif
+            @if(userCan('chapter-create', $book))
+                <a href="{{ $book->getUrl('/create-chapter') }}" class="icon-list-item">
+                    <span>@icon('add')</span>
+                    <span>{{ trans('entities.chapters_new') }}</span>
+                </a>
+            @endif
 
-    <div class="container small nopad">
-        <h1 class="break-text" v-pre>{{$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('/create-page') }}" class="button outline page">@icon('page'){{ 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('/create-chapter') }}" class="button outline chapter">@icon('chapter'){{ trans('entities.books_empty_add_chapter') }}</a>
-                        @endif
-                </div>
+            <hr class="primary-background">
+
+            @if(userCan('book-update', $book))
+                <a href="{{ $book->getUrl('/edit') }}" class="icon-list-item">
+                    <span>@icon('edit')</span>
+                    <span>{{ trans('common.edit') }}</span>
+                </a>
+                <a href="{{ $book->getUrl('/sort') }}" class="icon-list-item">
+                    <span>@icon('sort')</span>
+                    <span>{{ trans('common.sort') }}</span>
+                </a>
+            @endif
+            @if(userCan('restrictions-manage', $book))
+                <a href="{{ $book->getUrl('/permissions') }}" class="icon-list-item">
+                    <span>@icon('lock')</span>
+                    <span>{{ trans('entities.permissions') }}</span>
+                </a>
+            @endif
+            @if(userCan('book-delete', $book))
+                <a href="{{ $book->getUrl('/delete') }}" class="icon-list-item">
+                    <span>@icon('delete')</span>
+                    <span>{{ trans('common.delete') }}</span>
+                </a>
             @endif
 
-        </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">@icon('close'){{ trans('entities.search_clear') }}</a></h3>
-            <div v-if="!searchResults">
-                @include('partials/loading-icon')
+            <hr class="primary-background">
+
+            <div dropdown class="dropdown-container">
+                <div dropdown-toggle class="icon-list-item">
+                    <span>@icon('export')</span>
+                    <span>{{ trans('entities.export') }}</span>
+                </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>
             </div>
-            <div v-html="searchResults"></div>
         </div>
     </div>
 
 @stop
+
+@section('left')
+
+    @include('partials.entity-dashboard-search-box')
+
+    @if($book->tags->count() > 0)
+        <div class="mb-xl">
+            @include('components.tag-list', ['entity' => $book])
+        </div>
+    @endif
+
+    @if(count($activity) > 0)
+        <div class="mb-xl">
+            <h5>{{ trans('entities.recent_activity') }}</h5>
+            @include('partials.activity-list', ['activity' => $activity])
+        </div>
+    @endif
+@stop
+
index cff3c8984384b878096ca74798dd61cdbfe5db72..98f0af87eeeaa711408a6c010956bb68d9c273a3 100644 (file)
@@ -1,20 +1,48 @@
 <div class="sort-box" data-type="book" data-id="{{ $book->id }}">
-    <h3 class="text-book">@icon('book'){{ $book->name }}</h3>
+    <h5 class="text-book entity-list-item no-hover py-xs pl-none">
+        <span>@icon('book')</span>
+        <span>{{ $book->name }}</span>
+    </h5>
+    <div class="sort-box-options pb-sm">
+        <a href="#" data-sort="name" class="button outline small">{{ trans('entities.books_sort_name') }}</a>
+        <a href="#" data-sort="created" class="button outline small">{{ trans('entities.books_sort_created') }}</a>
+        <a href="#" data-sort="updated" class="button outline small">{{ trans('entities.books_sort_updated') }}</a>
+        <a href="#" data-sort="chaptersFirst" class="button outline small">{{ trans('entities.books_sort_chapters_first') }}</a>
+        <a href="#" data-sort="chaptersLast" class="button outline small">{{ trans('entities.books_sort_chapters_last') }}</a>
+    </div>
     <ul class="sortable-page-list sort-list">
+
         @foreach($bookChildren as $bookChild)
-            <li data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getClassName() }}" class="text-{{ $bookChild->getClassName() }}">
-                @icon($bookChild->isA('chapter') ? 'chapter' : 'page'){{ $bookChild->name }}
+            <li class="text-{{ $bookChild->getClassName() }}"
+                data-id="{{$bookChild->id}}" data-type="{{ $bookChild->getClassName() }}"
+                data-name="{{ $bookChild->name }}" data-created="{{ $bookChild->created_at->timestamp }}"
+                data-updated="{{ $bookChild->updated_at->timestamp }}">
+                <div class="entity-list-item">
+                    <span>@icon($bookChild->getType()) </span>
+                    <div>
+                        {{ $bookChild->name }}
+                        <div>
+
+                        </div>
+                    </div>
+                </div>
                 @if($bookChild->isA('chapter'))
                     <ul>
                         @foreach($bookChild->pages as $page)
-                            <li data-id="{{$page->id}}" class="text-page" data-type="page">
-                                @icon('page')
-                                {{ $page->name }}
+                            <li class="text-page"
+                                data-id="{{$page->id}}" data-type="page"
+                                data-name="{{ $page->name }}" data-created="{{ $page->created_at->timestamp }}"
+                                data-updated="{{ $page->updated_at->timestamp }}">
+                                <div class="entity-list-item">
+                                    <span>@icon('page')</span>
+                                    <span>{{ $page->name }}</span>
+                                </div>
                             </li>
                         @endforeach
                     </ul>
                 @endif
             </li>
         @endforeach
+
     </ul>
 </div>
\ No newline at end of file
index 77e95448f37292c40593257a342f8ca317bb2c11..cde37de4db165ed49dd05b731c3d06e18013878c 100644 (file)
@@ -1,53 +1,49 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('books._breadcrumbs', ['book' => $book])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container">
 
-        <div class="row">
-            <div class="col-md-8">
-                <div class="card">
-                    <h3>@icon('sort') {{ trans('entities.books_sort') }}</h3>
-                    <div class="body">
-                        <div id="sort-boxes">
-                            @include('books/sort-box', ['book' => $book, 'bookChildren' => $bookChildren])
-                        </div>
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $book->getUrl('/sort') => [
+                    'text' => trans('entities.books_sort'),
+                    'icon' => 'sort',
+                ]
+            ]])
+        </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 class="grid left-focus gap-xl">
+            <div>
+                <div class="card content-wrap">
+                    <h1 class="list-heading mb-l">{{ trans('entities.books_sort') }}</h1>
+                    <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 text-right">
+                            <a href="{{ $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                            <button class="button primary" type="submit">{{ trans('entities.books_sort_save') }}</button>
+                        </div>
+                    </form>
                 </div>
             </div>
-            @if(count($books) > 1)
-            <div class="col-md-4">
-                <div class="card">
-                    <h3>@icon('book') {{ 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">@icon('book'){{ $otherBook->name }}</a>
-                                </div>
-                            @endif
-                        @endforeach
-                    </div>
+
+            <div>
+                <div class="card content-wrap">
+                    <h2 class="list-heading mb-m">{{ trans('entities.books_sort_show_other') }}</h2>
+
+                    @include('components.entity-selector', ['name' => 'books_list', 'selectorSize' => 'compact', 'entityTypes' => 'book', 'entityPermission' => 'update', 'showAdd' => true])
+
                 </div>
             </div>
-            @endif
         </div>
+
     </div>
 
 @stop
     <script>
         $(document).ready(function() {
 
-            var sortableOptions = {
+            const $container = $('#sort-boxes');
+
+            // Sortable options
+            const sortableOptions = {
                 group: 'serialization',
-                onDrop: function($item, container, _super) {
-                    var pageMap = buildEntityMap();
-                    $('#sort-tree-input').val(JSON.stringify(pageMap));
+                containerSelector: 'ul',
+                itemPath: '',
+                itemSelector: 'li',
+                onDrop: function ($item, container, _super) {
+                    updateMapInput();
                     _super($item, container);
                 },
-                isValidTarget: function  ($item, container) {
+                isValidTarget: function ($item, container) {
                     // Prevent nested chapters
-                    return !($item.is('[data-type="chapter"]') && container.target.closest('li').attr('data-type') == 'chapter');
+                    return !($item.is('[data-type="chapter"]') && container.target.closest('li').attr('data-type') === 'chapter');
                 }
             };
 
-            var group = $('.sort-list').sortable(sortableOptions);
+            // Create our sortable group
+            let group = $('.sort-list').sortable(sortableOptions);
 
-            $('#additional-books').on('click', 'a', function(e) {
-                e.preventDefault();
-                var $link = $(this);
-                var url = $link.attr('href');
-                $.get(url, function(data) {
-                    $('#sort-boxes').append(data);
+            // Add book on selection confirm
+            window.$events.listen('entity-select-confirm', function(entityInfo) {
+                const alreadyAdded = $container.find(`[data-type="book"][data-id="${entityInfo.id}"]`).length > 0;
+                if (alreadyAdded) return;
+
+                const entitySortItemUrl = entityInfo.link + '/sort-item';
+                window.$http.get(entitySortItemUrl).then(resp => {
+                    $container.append(resp.data);
                     group.sortable("destroy");
-                    $('.sort-list').sortable(sortableOptions);
+                    group = $('.sort-list').sortable(sortableOptions);
                 });
-                $link.remove();
             });
 
+            /**
+             * Update the input with our sort data.
+             */
+            function updateMapInput() {
+                const pageMap = buildEntityMap();
+                $('#sort-tree-input').val(JSON.stringify(pageMap));
+            }
+
             /**
              * Build up a mapping of entities with their ordering and nesting.
              * @returns {Array}
              */
             function buildEntityMap() {
-                var entityMap = [];
-                var $lists = $('.sort-list');
+                const entityMap = [];
+                const $lists = $('.sort-list');
                 $lists.each(function(listIndex) {
-                    var list = $(this);
-                    var bookId = list.closest('[data-type="book"]').attr('data-id');
-                    var $directChildren = list.find('> [data-type="page"], > [data-type="chapter"]');
+                    const $list = $(this);
+                    const bookId = $list.closest('[data-type="book"]').attr('data-id');
+                    const $directChildren = $list.find('> [data-type="page"], > [data-type="chapter"]');
                     $directChildren.each(function(directChildIndex) {
-                        var $childElem = $(this);
-                        var type = $childElem.attr('data-type');
-                        var parentChapter = false;
-                        var childId = $childElem.attr('data-id');
+                        const $childElem = $(this);
+                        const type = $childElem.attr('data-type');
+                        const parentChapter = false;
+                        const childId = $childElem.attr('data-id');
+
                         entityMap.push({
                             id: childId,
                             sort: directChildIndex,
                             type: type,
                             book: bookId
                         });
-                        $chapterChildren = $childElem.find('[data-type="page"]').each(function(pageIndex) {
-                            var $chapterChild = $(this);
+
+                        $childElem.find('[data-type="page"]').each(function(pageIndex) {
+                            const $chapterChild = $(this);
                             entityMap.push({
                                 id: $chapterChild.attr('data-id'),
                                 sort: pageIndex,
                                 book: bookId
                             });
                         });
+
                     });
                 });
                 return entityMap;
             }
 
+
+            // Auto sort control
+            const sortOperations = {
+                name: function(a, b) {
+                    const aName = a.getAttribute('data-name').trim().toLowerCase();
+                    const bName = b.getAttribute('data-name').trim().toLowerCase();
+                    return aName.localeCompare(bName);
+                },
+                created: function(a, b) {
+                    const aTime = Number(a.getAttribute('data-created'));
+                    const bTime = Number(b.getAttribute('data-created'));
+                    return bTime - aTime;
+                },
+                updated: function(a, b) {
+                    const aTime = Number(a.getAttribute('data-update'));
+                    const bTime = Number(b.getAttribute('data-update'));
+                    return bTime - aTime;
+                },
+                chaptersFirst: function(a, b) {
+                    const aType = a.getAttribute('data-type');
+                    const bType = b.getAttribute('data-type');
+                    if (aType === bType) {
+                        return 0;
+                    }
+                    return (aType === 'chapter' ? -1 : 1);
+                },
+                chaptersLast: function(a, b) {
+                    const aType = a.getAttribute('data-type');
+                    const bType = b.getAttribute('data-type');
+                    if (aType === bType) {
+                        return 0;
+                    }
+                    return (aType === 'chapter' ? 1 : -1);
+                },
+            };
+
+            let lastSort = '';
+            let reverse = false;
+            const reversibleTypes = ['name', 'created', 'updated'];
+
+            $container.on('click', '.sort-box-options [data-sort]', function(event) {
+                event.preventDefault();
+                const $sortLists = $(this).closest('.sort-box').find('ul');
+                const sort = $(this).attr('data-sort');
+
+                reverse = (lastSort === sort) ? !reverse : false;
+                let sortFunction = sortOperations[sort];
+                if (reverse && reversibleTypes.includes(sort)) {
+                   sortFunction = function(a, b) {
+                       return 0 - sortOperations[sort](a, b)
+                   };
+                }
+
+                $sortLists.each(function() {
+                    const $list = $(this);
+                    $list.children('li').sort(sortFunction).appendTo($list);
+                });
+
+                lastSort = sort;
+                updateMapInput();
+            });
+
         });
     </script>
 @stop
diff --git a/resources/views/books/view-toggle.blade.php b/resources/views/books/view-toggle.blade.php
deleted file mode 100644 (file)
index 63eb9b9..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<form action="{{ baseUrl("/settings/users/{$currentUser->id}/switch-book-view") }}" method="POST" class="inline">
-    {!! csrf_field() !!}
-    {!! method_field('PATCH') !!}
-    <input type="hidden" value="{{ $booksViewType === 'list'? 'grid' : 'list' }}" name="view_type">
-    @if ($booksViewType === 'list')
-        <button type="submit" class="text-pos text-button">@icon('grid'){{ trans('common.grid_view') }}</button>
-    @else
-        <button type="submit" class="text-pos text-button">@icon('list'){{ trans('common.list_view') }}</button>
-    @endif
-</form>
\ No newline at end of file
diff --git a/resources/views/chapters/_breadcrumbs.blade.php b/resources/views/chapters/_breadcrumbs.blade.php
deleted file mode 100644 (file)
index 279f1c1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<div class="breadcrumbs">
-    @if (userCan('view', $chapter->book))
-    <a href="{{ $chapter->book->getUrl() }}" class="text-book text-button">@icon('book'){{ $chapter->book->getShortName() }}</a>
-    <span class="sep">&raquo;</span>
-    @endif
-    <a href="{{ $chapter->getUrl() }}" class="text-chapter text-button">@icon('chapter'){{$chapter->getShortName()}}</a>
-</div>
\ No newline at end of file
diff --git a/resources/views/chapters/child-menu.blade.php b/resources/views/chapters/child-menu.blade.php
new file mode 100644 (file)
index 0000000..36c7f9a
--- /dev/null
@@ -0,0 +1,12 @@
+<div class="chapter-child-menu">
+    <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
+        @icon('caret-right') @icon('page') <span>{{ trans_choice('entities.x_pages', $bookChild->pages->count()) }}</span>
+    </p>
+    <ul class="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' : '' }}">
+                @include('partials.entity-list-item-basic', ['entity' => $childPage, 'classes' => $current->matches($childPage)? 'selected' : '' ])
+            </li>
+        @endforeach
+    </ul>
+</div>
\ No newline at end of file
index 765756055c2392008698ed932ebad4cc7acd271c..fd2c82b46563e507f4b9647ddfb15fcc9ba8ac59 100644 (file)
@@ -1,26 +1,24 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        <div class="breadcrumbs">
-            <a href="{{ $book->getUrl() }}" class="text-book text-button">@icon('book'){{ $book->getShortName() }}</a>
-            <span class="sep">&raquo;</span>
-            <a href="{{ $book->getUrl('/create-chapter')}}" class="text-button">@icon('add'){{ trans('entities.chapters_create') }}</a>
-        </div>
-    </div>
-@stop
-
 @section('body')
-
     <div class="container small">
-        <div class="card">
-            <h3>@icon('add') {{ trans('entities.chapters_create') }}</h3>
-            <div class="body">
-                <form action="{{ $book->getUrl('/create-chapter') }}" method="POST">
-                    @include('chapters/form')
-                </form>
-            </div>
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $book->getUrl('create-chapter') => [
+                    'text' => trans('entities.chapters_create'),
+                    'icon' => 'add',
+                ]
+            ]])
+        </div>
+
+        <div class="content-wrap card">
+            <h1 class="list-heading">{{ trans('entities.chapters_create') }}</h1>
+            <form action="{{ $book->getUrl('/create-chapter') }}" method="POST">
+                @include('chapters.form')
+            </form>
         </div>
-    </div>
 
+    </div>
 @stop
\ No newline at end of file
index 727b4c9bb2b1771b62ec631f54670cf04b6f22d9..3444ee0fb19271f242352621d4c5ea3cd5799607 100644 (file)
@@ -1,29 +1,35 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('chapters._breadcrumbs', ['chapter' => $chapter])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ trans('entities.chapters_delete') }}</h3>
 
-            <div class="body">
-                <p>{{ trans('entities.chapters_delete_explain', ['chapterName' => $chapter->name]) }}</p>
-                <p class="text-neg">{{ trans('entities.chapters_delete_confirm') }}</p>
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $chapter->book,
+                $chapter,
+                $chapter->getUrl('/delete') => [
+                    'text' => trans('entities.chapters_delete'),
+                    'icon' => 'delete',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('entities.chapters_delete') }}</h1>
+            <p>{{ trans('entities.chapters_delete_explain', ['chapterName' => $chapter->name]) }}</p>
+            <p class="text-neg"><strong>{{ trans('entities.chapters_delete_confirm') }}</strong></p>
+
+            <form action="{{ $chapter->getUrl() }}" method="POST">
+
+                {!! csrf_field() !!}
+                <input type="hidden" name="_method" value="DELETE">
 
-                <form action="{{ $chapter->getUrl() }}" method="POST">
-                    {!! csrf_field() !!}
-                    <input type="hidden" name="_method" value="DELETE">
+                <div class="text-right">
                     <a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
-                    <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-                </form>
-            </div>
+                    <button type="submit" class="button primary">{{ trans('common.confirm') }}</button>
+                </div>
+            </form>
         </div>
     </div>
 
index 04c5aa724e17be000bfe6397b033ba2ae5adac41..d282fe1ddc9b614d5d40aede5f093dcacefa6792 100644 (file)
@@ -1,24 +1,28 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('chapters._breadcrumbs', ['chapter' => $chapter])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('edit') {{ 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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $book,
+                $chapter,
+                $chapter->getUrl('/edit') => [
+                    'text' => trans('entities.chapters_edit'),
+                    'icon' => 'edit'
+                ]
+            ]])
         </div>
+
+        <div class="content-wrap card">
+            <h1 class="list-heading">{{ trans('entities.chapters_edit') }}</h1>
+            <form action="{{  $chapter->getUrl() }}" method="POST">
+                <input type="hidden" name="_method" value="PUT">
+                @include('chapters.form', ['model' => $chapter])
+            </form>
+        </div>
+
     </div>
 
 @stop
\ No newline at end of file
index 8f710c0ecbdbd01e3f0b014e618a773d3bf508dd..2830855b4f8a0fc53a132ebda8cc9c19b5e818e9 100644 (file)
     @include('partials.custom-head')
 </head>
 <body>
-<div class="container">
-    <div class="row">
-        <div class="col-md-8 col-md-offset-2">
-            <div class="page-content">
-
-                <h1 style="font-size: 4.8em">{{$chapter->name}}</h1>
-
-                <p>{{ $chapter->description }}</p>
-
-                @if(count($pages) > 0)
-                <ul class="contents">
-                    @foreach($pages as $page)
-                        <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
-                    @endforeach
-                </ul>
-                @endif
-
-                @foreach($pages as $page)
-                    <div class="page-break"></div>
-                    <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
-                    {!! $page->html !!}
-                @endforeach
-
-            </div>
-        </div>
-    </div>
+
+<div class="page-content">
+
+    <h1 style="font-size: 4.8em">{{$chapter->name}}</h1>
+
+    <p>{{ $chapter->description }}</p>
+
+    @if(count($pages) > 0)
+        <ul class="contents">
+            @foreach($pages as $page)
+                <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
+            @endforeach
+        </ul>
+    @endif
+
+    @foreach($pages as $page)
+        <div class="page-break"></div>
+        <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
+        {!! $page->html !!}
+    @endforeach
+
 </div>
+
 </body>
 </html>
index fde46084449d31ec0fd732ad4d7043ee9fc6bedf..31d8597de44c60681153908ed9f834bd837056d4 100644 (file)
@@ -3,12 +3,12 @@
 
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form/text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name'])
 </div>
 
 <div class="form-group description-input">
     <label for="description">{{ trans('common.description') }}</label>
-    @include('form/textarea', ['name' => 'description'])
+    @include('form.textarea', ['name' => 'description'])
 </div>
 
 <div class="form-group" collapsible id="logo-control">
@@ -22,5 +22,5 @@
 
 <div class="form-group text-right">
     <a href="{{ isset($chapter) ? $chapter->getUrl() : $book->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
-    <button type="submit" class="button pos">{{ trans('entities.chapters_save') }}</button>
+    <button type="submit" class="button primary">{{ trans('entities.chapters_save') }}</button>
 </div>
index e5d8b207c85d76797d06c8f000a3c72910dea8ce..fd463e07a4fbfc51945c45678f2761e22cd8ef9f 100644 (file)
@@ -1,31 +1,22 @@
-<div class="chapter entity-list-item" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
-    <h4>
-        @if (isset($showPath) && $showPath)
-            <a href="{{ $chapter->book->getUrl() }}" class="text-book">
-                @icon('book'){{ $chapter->book->getShortName() }}
-            </a>
-            <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
-        @endif
-        <a href="{{ $chapter->getUrl() }}" class="text-chapter entity-list-item-link">
-            @icon('chapter')<span class="entity-list-item-name break-text">{{ $chapter->name }}</span>
-        </a>
-    </h4>
-
-    <div class="entity-item-snippet">
-        @if(isset($chapter->searchSnippet))
-            <p class="text-muted break-text">{!! $chapter->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $chapter->getExcerpt() }}</p>
-        @endif
+<a href="{{ $chapter->getUrl() }}" class="chapter entity-list-item @if($chapter->hasChildren()) has-children @endif" data-entity-type="chapter" data-entity-id="{{$chapter->id}}">
+    <span class="icon text-chapter">@icon('chapter')</span>
+    <div class="content">
+        <h4 class="entity-list-item-name break-text">{{ $chapter->name }}</h4>
+        <div class="entity-item-snippet">
+            <p class="text-muted break-text mb-s">{{ $chapter->getExcerpt() }}</p>
+        </div>
     </div>
-
-
-    @if(!isset($hidePages) && count($chapter->pages) > 0)
-        <p chapter-toggle class="text-muted">@icon('caret-right') @icon('page') <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">@icon('page'){{$page->name}}</a></h5>
-            @endforeach
+</a>
+@if ($chapter->hasChildren())
+    <div class="chapter chapter-expansion">
+        <span class="icon text-chapter">@icon('page')</span>
+        <div class="content">
+            <div chapter-toggle class="text-muted chapter-expansion-toggle">@icon('caret-right') <span>{{ trans_choice('entities.x_pages', $chapter->pages->count()) }}</span></div>
+            <div class="inset-list">
+                <div class="entity-list-item-children">
+                    @include('partials.entity-list', ['entities' => $chapter->pages])
+                </div>
+            </div>
         </div>
-    @endif
-</div>
\ No newline at end of file
+    </div>
+@endif
\ No newline at end of file
index 0efc18adf3c02cb28a9be16c6ff2b64d41189466..7f3de1322bdcaf94200ef5dfbeddbb358f190bf3 100644 (file)
@@ -1,30 +1,36 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('chapters._breadcrumbs', ['chapter' => $chapter])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
 
-        <div class="card">
-            <h3>@icon('folder') {{ 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', 'entityPermission' => 'chapter-create'])
-
-                    <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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $chapter->book,
+                $chapter,
+                $chapter->getUrl('/move') => [
+                    'text' => trans('entities.chapters_move'),
+                    'icon' => 'folder',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.chapters_move') }}</h1>
+
+            <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', 'entityPermission' => 'chapter-create'])
+
+                <div class="form-group text-right">
+                    <a href="{{ $chapter->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button primary">{{ trans('entities.chapters_move') }}</button>
+                </div>
+            </form>
+
         </div>
 
 
diff --git a/resources/views/chapters/permissions.blade.php b/resources/views/chapters/permissions.blade.php
new file mode 100644 (file)
index 0000000..cb5808e
--- /dev/null
@@ -0,0 +1,24 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container">
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $chapter->book,
+                $chapter,
+                $chapter->getUrl('/permissions') => [
+                    'text' => trans('entities.chapters_permissions'),
+                    'icon' => 'lock',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.chapters_permissions') }}</h1>
+            @include('form.entity-permissions', ['model' => $chapter])
+        </div>
+    </div>
+
+@stop
diff --git a/resources/views/chapters/restrictions.blade.php b/resources/views/chapters/restrictions.blade.php
deleted file mode 100644 (file)
index 70eb224..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-@extends('simple-layout')
-
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('chapters._breadcrumbs', ['chapter' => $chapter])
-    </div>
-@stop
-
-@section('body')
-
-    <div class="container">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('lock') {{ trans('entities.chapters_permissions') }}</h3>
-            <div class="body">
-                @include('form/restriction-form', ['model' => $chapter])
-            </div>
-        </div>
-    </div>
-
-@stop
index f5f9901450fc6aa41f2f4c4ed5558b5e2c242617..f74c584e5fa5b0da7491c1c917342a4b4afcdf44 100644 (file)
@@ -1,44 +1,4 @@
-@extends('sidebar-layout')
-
-@section('toolbar')
-    <div class="col-sm-6 col-xs-3 faded" v-pre>
-        @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">@icon('export'){{ trans('entities.export') }}</div>
-                <ul class="wide">
-                    <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
-                    <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
-                    <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">@icon('add'){{ trans('entities.pages_new') }}</a>
-            @endif
-            @if(userCan('chapter-update', $chapter))
-                <a href="{{ $chapter->getUrl('/edit') }}" class="text-primary text-button">@icon('edit'){{ trans('common.edit') }}</a>
-            @endif
-            @if((userCan('chapter-update', $chapter) && userCan('chapter-delete', $chapter) )|| userCan('restrictions-manage', $chapter) || userCan('chapter-delete', $chapter))
-                <div dropdown class="dropdown-container">
-                    <a dropdown-toggle class="text-primary text-button">@icon('more') {{ trans('common.more') }}</a>
-                    <ul>
-                        @if(userCan('chapter-update', $chapter) && userCan('chapter-delete', $chapter))
-                            <li><a href="{{ $chapter->getUrl('/move') }}" class="text-primary">@icon('folder'){{ trans('common.move') }}</a></li>
-                        @endif
-                        @if(userCan('restrictions-manage', $chapter))
-                            <li><a href="{{ $chapter->getUrl('/permissions') }}" class="text-primary">@icon('lock'){{ trans('entities.permissions') }}</a></li>
-                        @endif
-                        @if(userCan('chapter-delete', $chapter))
-                            <li><a href="{{ $chapter->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('common.delete') }}</a></li>
-                        @endif
-                    </ul>
-                </div>
-            @endif
-        </div>
-    </div>
-@stop
+@extends('tri-layout')
 
 @section('container-attrs')
     id="entity-dashboard"
     entity-type="chapter"
 @stop
 
-@section('sidebar')
+@section('body')
 
-    @if($chapter->tags->count() > 0)
-        <section>
-            @include('components.tag-list', ['entity' => $chapter])
-        </section>
-    @endif
+    <div class="mb-m">
+        @include('partials.breadcrumbs', ['crumbs' => [
+            $chapter->book,
+            $chapter,
+        ]])
+    </div>
 
-    <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">@icon('search')</button>
-                <button v-if="searching" v-cloak class="text-neg" @click="clearSearch()" type="button">@icon('close')</button>
-            </form>
+    <div class="content-wrap card">
+        <h1 class="break-text" v-pre>{{ $chapter->name }}</h1>
+        <div class="chapter-content" v-show="!searching">
+            <p v-pre class="text-muted break-text">{!! nl2br(e($chapter->description)) !!}</p>
+            @if(count($pages) > 0)
+                <div v-pre class="entity-list book-contents">
+                    @foreach($pages as $page)
+                        @include('pages.list-item', ['page' => $page])
+                    @endforeach
+                </div>
+            @else
+                <div class="mt-xl" v-pre>
+                    <hr>
+                    <p class="text-muted italic mb-m mt-xl">{{ trans('entities.chapters_empty') }}</p>
+
+                    <div class="icon-list block inline">
+                        @if(userCan('page-create', $chapter))
+                            <a href="{{ $chapter->getUrl('/create-page') }}" class="icon-list-item text-page">
+                                <span class="icon">@icon('page')</span>
+                                <span>{{ trans('entities.books_empty_create_page') }}</span>
+                            </a>
+                        @endif
+                        @if(userCan('book-update', $book))
+                            <a href="{{ $book->getUrl('/sort') }}" class="icon-list-item text-book">
+                                <span class="icon">@icon('book')</span>
+                                <span>{{ trans('entities.books_empty_sort_current_book') }}</span>
+                            </a>
+                        @endif
+                    </div>
+
+                </div>
+            @endif
         </div>
+
+        @include('partials.entity-dashboard-search-results')
     </div>
 
-    <div class="card entity-details">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
-        <div class="body blended-links text-small text-muted">
+@stop
+
+@section('right')
+
+    <div class="mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
+        <div class="blended-links text-small text-muted">
             @include('partials.entity-meta', ['entity' => $chapter])
 
             @if($book->restricted)
         </div>
     </div>
 
-    @include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
-@stop
+    <div class="actions mb-xl">
+        <h5>{{ trans('common.actions') }}</h5>
+        <div class="icon-list text-primary">
 
-@section('body')
+            @if(userCan('page-create', $chapter))
+                <a href="{{ $chapter->getUrl('/create-page') }}" class="icon-list-item">
+                    <span>@icon('add')</span>
+                    <span>{{ trans('entities.pages_new') }}</span>
+                </a>
+            @endif
 
-    <div class="container small nopad">
-        <h1 class="break-text" v-pre>{{ $chapter->name }}</h1>
-        <div class="chapter-content" v-show="!searching">
-            <p v-pre class="text-muted break-text">{!! nl2br(e($chapter->description)) !!}</p>
+            <hr class="primary-background"/>
 
-            @if(count($pages) > 0)
-                <div v-pre class="page-list">
-                    <hr>
-                    @foreach($pages as $page)
-                        @include('pages/list-item', ['page' => $page])
-                        <hr>
-                    @endforeach
-                </div>
-            @else
-                <div v-pre 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">@icon('page'){{ 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">@icon('book'){{ trans('entities.books_empty_sort_current_book') }}</a>
-                        @endif
-                    </p>
-                </div>
+            @if(userCan('chapter-update', $chapter))
+                <a href="{{ $chapter->getUrl('/edit') }}" class="icon-list-item">
+                    <span>@icon('edit')</span>
+                    <span>{{ trans('common.edit') }}</span>
+                </a>
             @endif
-        </div>
+            @if(userCan('chapter-update', $chapter) && userCan('chapter-delete', $chapter))
+                <a href="{{ $chapter->getUrl('/move') }}" class="icon-list-item">
+                    <span>@icon('folder')</span>
+                    <span>{{ trans('common.move') }}</span>
+                </a>
+            @endif
+            @if(userCan('restrictions-manage', $chapter))
+                <a href="{{ $chapter->getUrl('/permissions') }}" class="icon-list-item">
+                    <span>@icon('lock')</span>
+                    <span>{{ trans('entities.permissions') }}</span>
+                </a>
+            @endif
+            @if(userCan('chapter-delete', $chapter))
+                <a href="{{ $chapter->getUrl('/delete') }}" class="icon-list-item">
+                    <span>@icon('delete')</span>
+                    <span>{{ trans('common.delete') }}</span>
+                </a>
+            @endif
+
+            <hr class="primary-background"/>
 
-        <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">@icon('close'){{ trans('entities.search_clear') }}</a></h3>
-            <div v-if="!searchResults">
-                @include('partials/loading-icon')
+            <div dropdown class="dropdown-container">
+                <div dropdown-toggle class="icon-list-item">
+                    <span>@icon('export')</span>
+                    <span>{{ trans('entities.export') }}</span>
+                </div>
+                <ul class="wide">
+                    <li><a href="{{ $chapter->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+                    <li><a href="{{ $chapter->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+                    <li><a href="{{ $chapter->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
+                </ul>
             </div>
-            <div v-html="searchResults"></div>
         </div>
     </div>
+@stop
+
+@section('left')
+
+    @include('partials.entity-dashboard-search-box')
+
+    @if($chapter->tags->count() > 0)
+        <div class="mb-xl">
+            @include('components.tag-list', ['entity' => $chapter])
+        </div>
+    @endif
 
+    @include('partials.book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
 @stop
+
+
index 29d1292c206456730fd2e8970532b23edc202cef..b93e79ac819e4afaef1faf960fda7f304a1653fb 100644 (file)
@@ -1,55 +1,52 @@
-<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') }}">@icon('edit')</button>
-            @endif
-            @if(userCan('comment-create-all'))
-                <button type="button" class="text-button" action="reply" title="{{ trans('common.reply') }}">@icon('reply')</button>
-            @endif
-            @if(userCan('comment-delete', $comment))
-
-                <div dropdown class="dropdown-container">
-                    <button type="button" dropdown-toggle class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
-                    <ul>
-                        <li class="padded"><small class="text-muted">{{trans('entities.comment_delete_confirm')}}</small></li>
-                        <li><a action="delete" class="text-button neg" >@icon('delete'){{ 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 }}">
+<div class="comment-box mb-m" comment="{{ $comment->id }}" local-id="{{$comment->local_id}}" parent-id="{{$comment->parent_id}}" id="comment{{$comment->local_id}}">
+    <div class="header p-s">
+        <div class="grid half no-gap v-center">
+            <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
+                @endif
+            </div>
+            <div class="actions text-right">
+                @if(userCan('comment-update', $comment))
+                    <button type="button" class="text-button" action="edit" title="{{ trans('common.edit') }}">@icon('edit')</button>
+                @endif
+                @if(userCan('comment-create-all'))
+                    <button type="button" class="text-button" action="reply" title="{{ trans('common.reply') }}">@icon('reply')</button>
+                @endif
+                @if(userCan('comment-delete', $comment))
+                    <div dropdown class="dropdown-container">
+                        <button type="button" dropdown-toggle class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
+                        <ul>
+                            <li class="px-m text-small text-muted pb-s">{{trans('entities.comment_delete_confirm')}}</li>
+                            <li><a action="delete" class="text-button text-neg" >@icon('delete'){{ trans('common.delete') }}</a></li>
+                        </ul>
+                    </div>
+                @endif
+            </div>
         </div>
 
     </div>
 
     @if ($comment->parent_id)
-        <div class="reply-row primary-background-light text-muted">
+        <div class="reply-row primary-background-light text-muted px-s py-xs mb-s">
             {!! 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 comment-content class="content px-s pb-s">
         <div class="form-group loading" style="display: none;">
             @include('partials.loading-icon', ['text' => trans('entities.comment_deleting')])
         </div>
     </div>
 
     @if(userCan('comment-update', $comment))
-        <div comment-edit-container style="display: none;" class="content">
+        <div comment-edit-container style="display: none;" class="content px-s">
             <form novalidate>
-                <div class="form-group">
-                    <textarea name="markdown" rows="3" v-model="comment.text" placeholder="{{ trans('entities.comment_placeholder') }}">{{ $comment->text }}</textarea>
+                <div class="form-group description-input">
+                    <textarea name="markdown" rows="3" 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>
+                    <button type="submit" class="button primary">{{ trans('entities.comment_save') }}</button>
                 </div>
                 <div class="form-group loading" style="display: none;">
                     @include('partials.loading-icon', ['text' => trans('entities.comment_saving')])
index 5d3aafa1005244eda4706bf644f4f306b8ed2101..4848977c9cd16c55a527c91354cc61c3c61c1fd0 100644 (file)
@@ -1,5 +1,13 @@
 <div page-comments page-id="{{ $page->id }}" class="comments-list">
-  <h5 comments-title class="float left">{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
+    <div comment-count-bar class="grid half left-focus v-center">
+        <h5 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
+        @if (count($page->comments) === 0)
+            <div class="text-right" comment-add-button-container>
+                <button type="button" action="addComment"
+                        class="button outline">{{ trans('entities.comment_add') }}</button>
+            </div>
+        @endif
+    </div>
 
     <div class="comment-container" comment-container>
         @foreach($page->comments as $comment)
     </div>
 
     @if(userCan('comment-create-all'))
+        @include('comments.create')
+    @endif
 
-        <div class="comment-box" comment-box style="display:none;">
-            <div class="header">@icon('comment') {{ 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 float right">{{ trans('entities.comment_add') }}</button>
+    @if (count($page->comments) > 0)
+        <div class="text-right" comment-add-button-container>
+            <button type="button" action="addComment"
+                    class="button outline">{{ trans('entities.comment_add') }}</button>
         </div>
     @endif
 
diff --git a/resources/views/comments/create.blade.php b/resources/views/comments/create.blade.php
new file mode 100644 (file)
index 0000000..abd95f0
--- /dev/null
@@ -0,0 +1,29 @@
+<div class="comment-box" comment-box style="display:none;">
+    <div class="header p-s">{{ trans('entities.comment_new') }}</div>
+    <div comment-form-reply-to class="reply-row primary-background-light text-muted px-s py-xs mb-s" style="display: none;">
+        <div class="grid left-focus v-center">
+            <div>
+                {!! trans('entities.comment_in_reply_to', ['commentId' => '<a href=""></a>']) !!}
+            </div>
+            <div class="text-right">
+                <button class="text-button" action="remove-reply-to">{{ trans('common.remove') }}</button>
+            </div>
+        </div>
+    </div>
+    <div class="content px-s" comment-form-container>
+        <form novalidate>
+            <div class="form-group description-input">
+                        <textarea name="markdown" rows="3"
+                                  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 primary">{{ 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>
\ No newline at end of file
diff --git a/resources/views/common/header.blade.php b/resources/views/common/header.blade.php
new file mode 100644 (file)
index 0000000..c9c3015
--- /dev/null
@@ -0,0 +1,73 @@
+<header id="header" header-mobile-toggle class="primary-background">
+    <div class="grid mx-l">
+
+        <div>
+            <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
+                @if (setting('app-name-header'))
+                    <span class="logo-text">{{ setting('app-name') }}</span>
+                @endif
+            </a>
+            <div class="mobile-menu-toggle hide-over-l">@icon('more')</div>
+        </div>
+
+        <div class="header-search hide-under-l">
+            @if (hasAppAccess())
+            <form action="{{ baseUrl('/search') }}" method="GET" class="search-box">
+                <button id="header-search-box-button" type="submit">@icon('search') </button>
+                <input id="header-search-box-input" type="text" name="term" tabindex="2" placeholder="{{ trans('common.search') }}" value="{{ isset($searchTerm) ? $searchTerm : '' }}">
+            </form>
+            @endif
+        </div>
+
+        <div class="text-right">
+            <div class="header-links">
+                <div class="links text-center">
+                    @if (hasAppAccess())
+                        <a class="hide-over-l" href="{{ baseUrl('/search') }}">@icon('search'){{ trans('common.search') }}</a>
+                        @if(userCanOnAny('view', \BookStack\Entities\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
+                            <a href="{{ baseUrl('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
+                        @endif
+                        <a href="{{ baseUrl('/books') }}">@icon('books'){{ trans('entities.books') }}</a>
+                        @if(signedInUser() && userCan('settings-manage'))
+                            <a href="{{ baseUrl('/settings') }}">@icon('settings'){{ trans('settings.settings') }}</a>
+                        @endif
+                        @if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
+                            <a href="{{ baseUrl('/settings/users') }}">@icon('users'){{ trans('settings.users') }}</a>
+                        @endif
+                    @endif
+
+                    @if(!signedInUser())
+                        @if(setting('registration-enabled', false))
+                            <a href="{{ baseUrl("/register") }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
+                        @endif
+                        <a href="{{ baseUrl('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
+                    @endif
+                </div>
+                @if(signedInUser())
+                    <?php $currentUser = user(); ?>
+                    <div class="dropdown-container" dropdown>
+                        <span class="user-name hide-under-l" dropdown-toggle>
+                            <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
+                            <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
+                        </span>
+                        <ul>
+                            <li>
+                                <a href="{{ baseUrl("/user/{$currentUser->id}") }}">@icon('user'){{ trans('common.view_profile') }}</a>
+                            </li>
+                            <li>
+                                <a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}">@icon('edit'){{ trans('common.edit_profile') }}</a>
+                            </li>
+                            <li>
+                                <a href="{{ baseUrl('/logout') }}">@icon('logout'){{ trans('auth.logout') }}</a>
+                            </li>
+                        </ul>
+                    </div>
+                @endif
+            </div>
+        </div>
+
+    </div>
+</header>
\ No newline at end of file
index 2849d5ecdb427cc5a3631145eb17d35633d7486e..0efaa32ec7305c94526a201e55d28b3832b9d0b9 100644 (file)
@@ -1,18 +1,23 @@
-@extends('sidebar-layout')
+@extends('simple-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">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
-            @include('books/view-toggle', ['booksViewType' => $booksViewType])
-        </div>
-    </div>
-@stop
+@section('body')
+    <div class="container mt-m">
+        <div class="grid right-focus gap-xl">
+            <div>
 
-@section('sidebar')
-    @include('common/home-sidebar')
-@stop
+                <div class="actions mb-xl">
+                    <h5>{{ trans('common.actions') }}</h5>
+                    <div class="icon-list text-primary">
+                        @include('partials.view-toggle', ['view' => $view, 'type' => 'book'])
+                        @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                    </div>
+                </div>
 
-@section('body')
-    @include('books/list', ['books' => $books, 'bookViewType' => $booksViewType])
+                @include('common.home-sidebar')
+            </div>
+            <div>
+                @include('books.list', ['books' => $books, 'view' => $view])
+            </div>
+        </div>
+    </div>
 @stop
\ No newline at end of file
index 89154ee13ea4a87518d4981c73957d5bd8880f54..f1133a60755afa3ade5697d537dc017d5e54a9d8 100644 (file)
@@ -1,19 +1,26 @@
-@extends('sidebar-layout')
+@extends('simple-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">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
-        </div>
-    </div>
-@stop
+@section('body')
+    <div class="container mt-l">
+        <div class="grid right-focus gap-xl">
+            <div>
 
-@section('sidebar')
-    @include('common/home-sidebar')
-@stop
+                <div class="actions mb-xl">
+                    <h5>{{ trans('common.actions') }}</h5>
+                    <div class="icon-list text-primary">
+                        @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                    </div>
+                </div>
 
-@section('body')
-    <div class="page-content" page-display="{{ $customHomepage->id }}">
-        @include('pages/page-display', ['page' => $customHomepage])
+                @include('common.home-sidebar')
+            </div>
+            <div>
+                <div class="content-wrap card">
+                    <div class="page-content" page-display="{{ $customHomepage->id }}">
+                        @include('pages.page-display', ['page' => $customHomepage])
+                    </div>
+                </div>
+            </div>
+        </div>
     </div>
-@stop
+@stop
\ No newline at end of file
index 3ae055b33a6314260a1d639f0963105606b55649..f09bfd4169659d136e54544bdcf620a82e67b114 100644 (file)
@@ -1,18 +1,23 @@
-@extends('sidebar-layout')
+@extends('simple-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">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
-            @include('shelves/view-toggle', ['shelvesViewType' => $shelvesViewType])
-        </div>
-    </div>
-@stop
+@section('body')
+    <div class="container mt-m">
+        <div class="grid right-focus gap-xl">
+            <div>
 
-@section('sidebar')
-    @include('common/home-sidebar')
-@stop
+                <div class="actions mb-xl">
+                    <h5>{{ trans('common.actions') }}</h5>
+                    <div class="icon-list text-primary">
+                        @include('partials.view-toggle', ['view' => $view, 'type' => 'shelf'])
+                        @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                    </div>
+                </div>
 
-@section('body')
-    @include('shelves/list', ['shelves' => $shelves, 'shelvesViewType' => $shelvesViewType])
+                @include('common.home-sidebar')
+            </div>
+            <div>
+                @include('shelves.list', ['shelves' => $shelves, 'view' => $view])
+            </div>
+        </div>
+    </div>
 @stop
\ No newline at end of file
index 2210294814c0ac4ef859dd5ab488c7f2cfdd83b4..07eda2cff997ac848a1c7f63b1720d8ce6226212 100644 (file)
@@ -1,23 +1,23 @@
 @if(count($draftPages) > 0)
-    <div id="recent-drafts" class="card">
-        <h3>@icon('edit') {{ trans('entities.my_recent_drafts') }}</h3>
-        @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
+    <div id="recent-drafts" class="mb-xl">
+        <h5>{{ trans('entities.my_recent_drafts') }}</h5>
+        @include('partials.entity-list', ['entities' => $draftPages, 'style' => 'compact'])
     </div>
 @endif
 
-<div class="card">
-    <h3>@icon($signedIn ? 'view' : 'star-circle') {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
-    @include('partials/entity-list', [
+<div class="mb-xl">
+    <h5>{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h5>
+    @include('partials.entity-list', [
         'entities' => $recents,
         'style' => 'compact',
         'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
         ])
 </div>
 
-<div class="card">
-    <h3>@icon('file') <a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
+<div class="mb-xl">
+    <h5><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h5>
     <div id="recently-updated-pages">
-        @include('partials/entity-list', [
+        @include('partials.entity-list', [
         'entities' => $recentlyUpdatedPages,
         'style' => 'compact',
         'emptyText' => trans('entities.no_pages_recently_updated')
@@ -25,7 +25,7 @@
     </div>
 </div>
 
-<div id="recent-activity" class="card">
-    <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
-    @include('partials/activity-list', ['activity' => $activity])
+<div id="recent-activity" class="mb-xl">
+    <h5>{{ trans('entities.recent_activity') }}</h5>
+    @include('partials.activity-list', ['activity' => $activity])
 </div>
\ No newline at end of file
index cc20fc68e2d91d97a7dc89236bb82e052f86fb9b..35e45c9c86fd43534e51aa50ec4e3895b4751f97 100644 (file)
@@ -1,41 +1,42 @@
 @extends('simple-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">@icon('expand-text'){{ trans('common.toggle_details') }}</a>
+@section('body')
+
+    <div class="container px-xl py-s">
+        <div class="icon-list inline block">
+            @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
         </div>
     </div>
-@stop
-
-@section('body')
 
     <div class="container" id="home-default">
-        <div class="row">
-
-            <div class="col-sm-4">
+        <div class="grid third gap-xxl no-row-gap" >
+            <div>
                 @if(count($draftPages) > 0)
-                    <div id="recent-drafts" class="card">
-                        <h3>@icon('edit') {{ trans('entities.my_recent_drafts') }}</h3>
-                        @include('partials/entity-list', ['entities' => $draftPages, 'style' => 'compact'])
+                    <div id="recent-drafts" class="card mb-xl">
+                        <h3>{{ trans('entities.my_recent_drafts') }}</h3>
+                        <div class="px-m">
+                            @include('partials.entity-list', ['entities' => $draftPages, 'style' => 'compact'])
+                        </div>
                     </div>
                 @endif
 
-                <div class="card">
-                    <h3>@icon($signedIn ? 'view' : 'star-circle') {{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
-                    @include('partials/entity-list', [
+                <div id="{{ $signedIn ? 'recently-viewed' : 'recent-books' }}" class="card mb-xl">
+                    <h3>{{ trans('entities.' . ($signedIn ? 'my_recently_viewed' : 'books_recent')) }}</h3>
+                    <div class="px-m">
+                        @include('partials.entity-list', [
                         'entities' => $recents,
                         'style' => 'compact',
                         'emptyText' => $signedIn ? trans('entities.no_pages_viewed') : trans('entities.books_empty')
                         ])
+                    </div>
                 </div>
             </div>
 
-            <div class="col-sm-4">
-                <div class="card">
-                    <h3>@icon('file') <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', [
+            <div>
+                <div id="recent-pages" class="card mb-xl">
+                    <h3><a class="no-color" href="{{ baseUrl("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
+                    <div id="recently-updated-pages" class="px-m">
+                        @include('partials.entity-list', [
                         'entities' => $recentlyUpdatedPages,
                         'style' => 'compact',
                         'emptyText' => trans('entities.no_pages_recently_updated')
                 </div>
             </div>
 
-            <div class="col-sm-4" id="recent-activity">
-                <div class="card">
-                    <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
-                    @include('partials/activity-list', ['activity' => $activity])
+            <div>
+                <div id="recent-activity">
+                    <div class="card mb-xl">
+                        <h3>{{ trans('entities.recent_activity') }}</h3>
+                        @include('partials.activity-list', ['activity' => $activity])
+                    </div>
                 </div>
             </div>
 
@@ -55,4 +58,5 @@
     </div>
 
 
+
 @stop
index 67e7b77737049504ee3e38a4f00b939c66d1e619..7636cd581b63c8c8923ed96ea3101039d54332b0 100644 (file)
@@ -4,10 +4,10 @@
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.code_editor') }}</div>
-                <button class="overlay-close neg corner-button button" @click="hide()">x</button>
+                <button class="popup-header-close" @click="hide()">x</button>
             </div>
 
-            <div class="padded popup-content">
+            <div class="p-l popup-content">
                 <div class="form-group">
                     <label for="code-editor-language">{{ trans('components.code_language') }}</label>
                     <div class="lang-options">
@@ -43,7 +43,7 @@
                 </div>
 
                 <div class="form-group">
-                    <button type="button" class="button pos" @click="save()">{{ trans('components.code_save') }}</button>
+                    <button type="button" class="button primary" @click="save()">{{ trans('components.code_save') }}</button>
                 </div>
 
             </div>
diff --git a/resources/views/components/custom-checkbox.blade.php b/resources/views/components/custom-checkbox.blade.php
new file mode 100644 (file)
index 0000000..73b7496
--- /dev/null
@@ -0,0 +1,11 @@
+{{--
+$name
+$value
+$checked
+$label
+--}}
+<label class="toggle-switch @if($errors->has($name)) text-neg @endif">
+    <input type="checkbox" name="{{$name}}" value="{{ $value }}" @if($checked) checked="checked" @endif>
+    <span class="custom-checkbox text-primary">@icon('check')</span>
+    <span class="label">{{$label}}</span>
+</label>
\ No newline at end of file
index ecd03c80f3fd32e66f5c82a33f782e90e1bbbaa0..c497a16d594563db25c1eb6e0e902857d7814c57 100644 (file)
@@ -1,13 +1,13 @@
 <div id="entity-selector-wrap">
     <div overlay entity-selector-popup>
-        <div class="popup-body small flex-child">
+        <div class="popup-body small">
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
-                <button type="button" class="corner-button neg button overlay-close">x</button>
+                <button type="button" class="popup-header-close">x</button>
             </div>
             @include('components.entity-selector', ['name' => 'entity-selector'])
             <div class="popup-footer">
-                <button type="button" disabled="true" class="button entity-link-selector-confirm pos corner-button">{{ trans('common.select') }}</button>
+                <button type="button" disabled="true" class="button entity-link-selector-confirm primary corner-button">{{ trans('common.select') }}</button>
             </div>
         </div>
     </div>
index 89c574c28c0f3090868cc43ef1f3c6794f7b6d4c..cb41950cb0e1426cc21100be417613e6187a1d33 100644 (file)
@@ -1,8 +1,14 @@
-<div class="form-group">
-    <div entity-selector class="entity-selector {{$selectorSize or ''}}" entity-types="{{ $entityTypes or 'book,chapter,page' }}" entity-permission="{{ $entityPermission or 'view' }}">
+<div class="form-group entity-selector-container">
+    <div entity-selector class="entity-selector {{$selectorSize ?? ''}}" entity-types="{{ $entityTypes ?? 'book,chapter,page' }}" entity-permission="{{ $entityPermission ?? 'view' }}">
         <input type="hidden" entity-selector-input name="{{$name}}" value="">
         <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>
+        @if($showAdd ?? false)
+            <div class="entity-selector-add">
+                <button entity-selector-add-button type="button"
+                        class="button outline">@icon('add'){{ trans('common.add') }}</button>
+            </div>
+        @endif
     </div>
 </div>
\ No newline at end of file
diff --git a/resources/views/components/expand-toggle.blade.php b/resources/views/components/expand-toggle.blade.php
new file mode 100644 (file)
index 0000000..e8b2220
--- /dev/null
@@ -0,0 +1,19 @@
+{{--
+$target - CSS selector of items to expand
+$key - Unique key for checking existing stored state.
+--}}
+<?php $isOpen = setting()->getForCurrentUser('section_expansion#'. $key); ?>
+<a expand-toggle="{{ $target }}"
+   expand-toggle-update-endpoint="{{ baseUrl('/settings/users/'. $currentUser->id .'/update-expansion-preference/' . $key) }}"
+   expand-toggle-is-open="{{ $isOpen ? 'yes' : 'no' }}"
+   class="text-muted icon-list-item text-primary">
+    <span>@icon('expand-text')</span>
+    <span>{{ trans('common.toggle_details') }}</span>
+</a>
+@if($isOpen)
+    @push('head')
+        <style>
+            {{ $target }} {display: block;}
+        </style>
+    @endpush
+@endif
\ No newline at end of file
index eca35b8aaad5f7a38af25a9941ccac083981c9e1..df577b54525cc0b416986cd18508067eed8056cb 100644 (file)
@@ -1,21 +1,19 @@
-<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to or 0 }}">
+<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to ?? 0 }}">
     <div overlay v-cloak @click="hide">
         <div class="popup-body" @click.stop="">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="overlay-close neg corner-button button" @click="hide()">x</button>
+                <button class="popup-header-close" @click="hide()">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
 
                 <div class="image-manager-content">
-                    <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') }}" :class="{selected: (view=='all')}" @click="setView('all')">@icon('images') {{ 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')">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</div>
-                            <div class="col-xs-4 tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (view=='page')}" @click="setView('page')">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</div>
-                        </div>
+                    <div v-if="imageType === 'gallery'" class="image-manager-header primary-background-light nav-tabs grid third">
+                        <div class="tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: (view=='all')}" @click="setView('all')">@icon('images') {{ trans('components.image_all') }}</div>
+                        <div class="tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (view=='book')}" @click="setView('book')">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</div>
+                        <div class="tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (view=='page')}" @click="setView('page')">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</div>
                     </div>
                     <div v-show="view === 'all'" >
                         <form @submit.prevent="searchImages" class="contained-search-box">
index 034b65cc5f64389541f13ac323f12e0dd39604b1..e7c74c786361b559250fc09d2b2119643cabb86a 100644 (file)
@@ -1,17 +1,20 @@
-<div class="image-picker" image-picker="{{$name}}" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId or '' }}" data-resize-crop="{{ $resizeCrop or '' }}">
+<div class="image-picker" image-picker="{{$name}}" data-default-image="{{ $defaultImage }}" data-resize-height="{{ $resizeHeight }}" data-resize-width="{{ $resizeWidth }}" data-current-id="{{ $currentId ?? '' }}" data-resize-crop="{{ $resizeCrop ?? '' }}">
 
-    <div>
-        <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif  class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
-    </div>
-
-    <button class="button" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
-    <br>
-    <button class="text-button" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
+    <div class="grid half">
+        <div class="text-center">
+            <img @if($currentImage && $currentImage !== 'none') src="{{$currentImage}}" @else src="{{$defaultImage}}" @endif  class="{{$imageClass}} @if($currentImage=== 'none') none @endif" alt="{{ trans('components.image_preview') }}">
+        </div>
+        <div class="text-center">
+            <button class="button outline small" type="button" data-action="show-image-manager">{{ trans('components.image_select_image') }}</button>
+            <br>
+            <button class="text-button text-muted" data-action="reset-image" type="button">{{ trans('common.reset') }}</button>
 
-    @if ($showRemove)
-        <span class="sep">|</span>
-        <button class="text-button neg" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
-    @endif
+            @if ($showRemove)
+                <span class="sep">|</span>
+                <button class="text-button text-muted" data-action="remove-image" type="button">{{ trans('common.remove') }}</button>
+            @endif
+        </div>
+    </div>
 
     <input type="hidden" name="{{$name}}" id="{{$name}}" value="{{ isset($currentId) && ($currentId !== 0 && $currentId !== false) ? $currentId : $currentImage}}">
 </div>
\ No newline at end of file
index 801919a14c8f644af6263ad6cd30d6a8d0e0c6a8..5ae3831986bc8aa8eeee34a09f72275f63f4811a 100644 (file)
@@ -1,6 +1,6 @@
 <div id="tag-manager" entity-id="{{ isset($entity) ? $entity->id : 0 }}" entity-type="{{ $entity ? $entity->getType() : $entityType }}">
     <div class="tags">
-        <p class="muted small">{!! nl2br(e(trans('entities.tags_explain'))) !!}</p>
+        <p class="text-muted small">{!! nl2br(e(trans('entities.tags_explain'))) !!}</p>
 
 
         <draggable :options="{handle: '.handle'}" :list="tags" element="div">
index c5a30a60f9dae1842a75fcbc9ee3418b52584d18..84a8a3083716972b1a1a53a6302aec62d3ed6256 100644 (file)
@@ -1,4 +1,6 @@
-<div toggle-switch="{{$name}}" class="toggle-switch @if($value) active @endif">
+<label toggle-switch="{{$name}}" class="toggle-switch">
     <input type="hidden" name="{{$name}}" value="{{$value?'true':'false'}}"/>
-    <div class="switch-handle"></div>
-</div>
\ No newline at end of file
+    <input type="checkbox" @if($value) checked="checked" @endif>
+    <span class="custom-checkbox text-primary">@icon('check')</span>
+    <span class="label">{{ $label }}</span>
+</label>
\ No newline at end of file
index 9c431828a810f6b260e9bd91c2246d51c78d90b9..e13e1fce9e09ebf51cd0e392da100aafbdca849d 100644 (file)
@@ -1,36 +1,45 @@
 @extends('simple-layout')
 
 @section('content')
-<div class="container">
+<div class="container mt-l">
 
-    <p>&nbsp;</p>
-
-    <div class="card">
-        <h3>@icon('danger') {{ $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 class="card mb-xl px-l pb-xl pt-l">
+        <div class="grid half v-center">
+            <div>
+                <h1 class="list-heading">{{ $message ?? trans('errors.404_page_not_found') }}</h1>
+                <h5>{{ trans('errors.sorry_page_not_found') }}</h5>
+            </div>
+            <div class="text-right">
+                <a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a>
+            </div>
         </div>
+
     </div>
 
     @if (setting('app-public') || !user()->isDefault())
-        <div class="row">
-            <div class="col-md-4">
-                <div class="card">
-                    <h3 class="text-muted">@icon('page') {{ trans('entities.pages_popular') }}</h3>
-                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Entities\Page::class]), 'style' => 'compact'])
+        <div class="grid third gap-xxl">
+            <div>
+                <div class="card mb-xl">
+                    <h3>{{ trans('entities.pages_popular') }}</h3>
+                    <div class="px-m">
+                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, 'page'), 'style' => 'compact'])
+                    </div>
                 </div>
             </div>
-            <div class="col-md-4">
-                <div class="card">
-                    <h3 class="text-muted">@icon('book') {{ trans('entities.books_popular') }}</h3>
-                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Entities\Book::class]), 'style' => 'compact'])
+            <div>
+                <div class="card mb-xl">
+                    <h3>{{ trans('entities.books_popular') }}</h3>
+                    <div class="px-m">
+                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, 'book'), 'style' => 'compact'])
+                    </div>
                 </div>
             </div>
-            <div class="col-md-4">
-                <div class="card">
-                    <h3 class="text-muted">@icon('chapter') {{ trans('entities.chapters_popular') }}</h3>
-                    @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, [\BookStack\Entities\Chapter::class]), 'style' => 'compact'])
+            <div>
+                <div class="card mb-xl">
+                    <h3>{{ trans('entities.chapters_popular') }}</h3>
+                    <div class="px-m">
+                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, 'chapter'), 'style' => 'compact'])
+                    </div>
                 </div>
             </div>
         </div>
index a01234d811703a4d069a1fd915c23208d2a837b4..3745f2292f733ce4c18f1af01d6f9e0130c7c288 100644 (file)
@@ -6,7 +6,7 @@
         <div class="card">
             <h3 class="text-muted">{{ trans('errors.error_occurred') }}</h3>
             <div class="body">
-                <h5>{{ $message or 'An unknown error occurred' }}</h5>
+                <h5>{{ $message ?? 'An unknown error occurred' }}</h5>
                 <p><a href="{{ baseUrl('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
             </div>
         </div>
index f58856ced4d0f052129acfc8e2245574cb5d6557..29364606b97d24f58cc977d7adc012fd6bb3a788 100644 (file)
@@ -1,13 +1,11 @@
-@extends('public')
+@extends('simple-layout')
 
 @section('content')
 
-    <div class="container small">
-        <div class="card">
-            <div class="body">
-                <h4 class="text-muted">@icon('danger') {{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h4>
-                <p>{{ trans('errors.back_soon') }}</p>
-            </div>
+    <div class="container small mt-xl">
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('errors.app_down', ['appName' => setting('app-name')]) }}</h1>
+            <p>{{ trans('errors.back_soon') }}</p>
         </div>
     </div>
 
index 25589690600860350c08073891d83e82826b2cd4..255b591aa9ff790ffef3158dedbd477c48101716 100644 (file)
@@ -1,11 +1,15 @@
-
-<label>
-    <input value="true" id="{{$name}}" type="checkbox" name="{{$name}}"
-           @if($errors->has($name)) class="neg" @endif
-           @if(old($name) || (!old() && isset($model) && $model->$name)) checked="checked" @endif
-    >
-    {{ $label }}
-</label>
+{{--
+$name
+$label
+$errors?
+$model?
+--}}
+@include('components.custom-checkbox', [
+    'name' => $name,
+    'label' => $label,
+    'value' => 'true',
+    'checked' => old($name) || (!old() && isset($model) && $model->$name)
+])
 
 @if($errors->has($name))
     <div class="text-neg text-small">{{ $errors->first($name) }}</div>
diff --git a/resources/views/form/delete-button.blade.php b/resources/views/form/delete-button.blade.php
deleted file mode 100644 (file)
index 6c53eff..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<form action="{{$url}}" method="POST" class="inline">
-    {{ csrf_field() }}
-    <input type="hidden" name="_method" value="DELETE">
-    <button type="submit" class="button neg">{{ isset($text) ? $text : trans('common.delete') }}</button>
-</form>
\ No newline at end of file
diff --git a/resources/views/form/entity-permissions.blade.php b/resources/views/form/entity-permissions.blade.php
new file mode 100644 (file)
index 0000000..6bb4b51
--- /dev/null
@@ -0,0 +1,42 @@
+<form action="{{ $model->getUrl('/permissions') }}" method="POST">
+    {!! csrf_field() !!}
+    <input type="hidden" name="_method" value="PUT">
+
+    <p class="mb-none">{{ trans('entities.permissions_intro') }}</p>
+
+    <div class="form-group">
+        @include('form.checkbox', [
+            'name' => 'restricted',
+            'label' => trans('entities.permissions_enable'),
+        ])
+    </div>
+
+    <table permissions-table class="table permissions-table toggle-switch-list">
+        <tr>
+            <th>{{ trans('common.role') }}</th>
+            <th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>
+                {{ trans('common.actions') }}
+                <a href="#" permissions-table-toggle-all class="text-small ml-m text-primary">{{ trans('common.toggle_all') }}</a>
+            </th>
+        </tr>
+        @foreach($roles as $role)
+            <tr>
+                <td width="33%" class="pt-m">
+                    {{ $role->display_name }}
+                    <a href="#" permissions-table-toggle-all-in-row class="text-small float right ml-m text-primary">{{ trans('common.toggle_all') }}</a>
+                </td>
+                <td>@include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.view'), 'action' => 'view'])</td>
+                @if(!$model->isA('page'))
+                    <td>@include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.create'), 'action' => 'create'])</td>
+                @endif
+                <td>@include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.update'), 'action' => 'update'])</td>
+                <td>@include('form.restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.delete'), 'action' => 'delete'])</td>
+            </tr>
+        @endforeach
+    </table>
+
+    <div class="text-right">
+        <a href="{{ $model->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+        <button type="submit" class="button primary">{{ trans('entities.permissions_save') }}</button>
+    </div>
+</form>
\ No newline at end of file
index 0b6a5e04382b0f5df79435f289a81a95ccd73485..8f7c4ee01e5c8384596884f7e390f9d306c293c0 100644 (file)
@@ -1,5 +1,5 @@
 <input type="password" id="{{ $name }}" name="{{ $name }}"
-       @if($errors->has($name)) class="neg" @endif
+       @if($errors->has($name)) class="text-neg" @endif
        @if(isset($placeholder)) placeholder="{{$placeholder}}" @endif
        @if(isset($tabindex)) tabindex="{{$tabindex}}" @endif
        @if(old($name)) value="{{ old($name)}}" @endif>
index 5a8662b56a964430fbdcdc2937264db7b0344bf2..65a94239e78d83d5b9faadd8b7eafcaa627aaac3 100644 (file)
@@ -1,6 +1,13 @@
-
-<label>
-    <input value="true" id="{{$name}}[{{$role->id}}][{{$action}}]" type="checkbox" name="{{$name}}[{{$role->id}}][{{$action}}]"
-           @if(isset($model) && $model->hasRestriction($role->id, $action)) checked="checked" @endif>
-    {{ $label }}
-</label>
\ No newline at end of file
+{{--
+$name
+$label
+$role
+$action
+$model?
+--}}
+@include('components.custom-checkbox', [
+    'name' => $name . '[' . $role->id . '][' . $action . ']',
+    'label' => $label,
+    'value' => 'true',
+    'checked' => isset($model) && $model->hasRestriction($role->id, $action)
+])
\ No newline at end of file
diff --git a/resources/views/form/restriction-form.blade.php b/resources/views/form/restriction-form.blade.php
deleted file mode 100644 (file)
index c32fe00..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<form action="{{ $model->getUrl('/permissions') }}" method="POST">
-    {!! csrf_field() !!}
-    <input type="hidden" name="_method" value="PUT">
-
-    <p>{{ trans('entities.permissions_intro') }}</p>
-
-    <div class="form-group">
-        @include('form/checkbox', ['name' => 'restricted', 'label' => trans('entities.permissions_enable')])
-    </div>
-
-
-    <table class="table">
-        <tr>
-            <th>{{ trans('common.role') }}</th>
-            <th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>{{ trans('common.actions') }}</th>
-        </tr>
-        @foreach($roles as $role)
-            <tr>
-                <td>{{ $role->display_name }}</td>
-                <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.view'), 'action' => 'view'])</td>
-                @if(!$model->isA('page'))
-                    <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.create'), 'action' => 'create'])</td>
-                @endif
-                <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.update'), 'action' => 'update'])</td>
-                <td>@include('form/restriction-checkbox', ['name'=>'restrictions', 'label' => trans('common.delete'), 'action' => 'delete'])</td>
-            </tr>
-        @endforeach
-    </table>
-
-    <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
index df868ee98055939f160d0485b3e13748c0a7ad5b..580b332f39c7e1fc993c670693ba08a8ad818192 100644 (file)
@@ -1,13 +1,16 @@
 
-@foreach($roles as $role)
-    <label>
-        <input value="{{ $role->id }}" id="{{$name}}-{{$role->name}}" type="checkbox" name="{{$name}}[{{$role->name}}]"
-               @if($errors->has($name)) class="neg" @endif
-               @if(old($name . '.' . $role->name) || (!old('name') && isset($model) && $model->hasRole($role->name))) checked="checked" @endif
-        >
-        {{ $role->display_name }}
-    </label>
-@endforeach
+<div class="toggle-switch-list dual-column-content">
+    @foreach($roles as $role)
+        <div>
+            @include('components.custom-checkbox', [
+                'name' => $name . '[' . str_replace('.', 'DOT', $role->name) . ']',
+                'label' => $role->display_name,
+                'value' => $role->id,
+                'checked' => old($name . '.' . str_replace('.', 'DOT', $role->name)) || (!old('name') && isset($model) && $model->hasRole($role->name))
+            ])
+        </div>
+    @endforeach
+</div>
 
 @if($errors->has($name))
     <div class="text-neg text-small">{{ $errors->first($name) }}</div>
index 036ba7847d98ed8faaedf5e53d2f9c4827bf874b..eed9cb2f192c749377a74ce5d0f6018c951e4a43 100644 (file)
@@ -2,7 +2,7 @@
 <select id="{{ $name }}" name="{{ $name }}">
     @foreach($options as $option)
         <option value="{{$option->id}}"
-                @if($errors->has($name)) class="neg" @endif
+                @if($errors->has($name)) class="text-neg" @endif
                 @if(isset($model) || old($name)) @if(old($name) && old($name) === $option->id) selected @elseif(isset($model) && $model->role->id === $option->id) selected @endif @endif
                 >
             {{ $option->display_name }}
index 3379b69b7f2fcb1bf89cb75962b7e03d99199ae6..948a55cbc10a2b6e3f68e16626f8a0ff785394a8 100644 (file)
@@ -1,5 +1,5 @@
 <input type="text" id="{{ $name }}" name="{{ $name }}"
-       @if($errors->has($name)) class="neg" @endif
+       @if($errors->has($name)) class="text-neg" @endif
        @if(isset($placeholder)) placeholder="{{$placeholder}}" @endif
        @if(isset($tabindex)) tabindex="{{$tabindex}}" @endif
        @if(isset($model) || old($name)) value="{{ old($name) ? old($name) : $model->$name}}" @endif>
index c3b784d93005fdfd20563b012c79333c39be86ca..8220a0c3711b3158a17f65ce42a02003cfcd28b2 100644 (file)
@@ -1,5 +1,5 @@
 <textarea id="{{ $name }}" name="{{ $name }}" rows="5"
-          @if($errors->has($name)) class="neg" @endif>@if(isset($model) || old($name)){{ old($name) ? old($name) : $model->$name}}@endif</textarea>
+          @if($errors->has($name)) class="text-neg" @endif>@if(isset($model) || old($name)){{ old($name) ? old($name) : $model->$name}}@endif</textarea>
 @if($errors->has($name))
     <div class="text-neg text-small">{{ $errors->first($name) }}</div>
 @endif
\ No newline at end of file
diff --git a/resources/views/pages/_breadcrumbs.blade.php b/resources/views/pages/_breadcrumbs.blade.php
deleted file mode 100644 (file)
index 19bab40..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-<div class="breadcrumbs">
-    @if (userCan('view', $page->book))
-        <a href="{{ $page->book->getUrl() }}" class="text-book text-button">@icon('book'){{ $page->book->getShortName() }}</a>
-        <span class="sep">&raquo;</span>
-    @endif
-    @if($page->hasChapter() && userCan('view', $page->chapter))
-        <a href="{{ $page->chapter->getUrl() }}" class="text-chapter text-button">
-            @icon('chapter')
-            {{ $page->chapter->getShortName() }}
-        </a>
-        <span class="sep">&raquo;</span>
-    @endif
-    <a href="{{ $page->getUrl() }}" class="text-page text-button">@icon('page'){{ $page->getShortName() }}</a>
-</div>
\ No newline at end of file
index eb6afcad2ebf9010ebd5d81ab8eca9810e61251a..f197421725874c893ac27a0c4dd95352346a06b1 100644 (file)
@@ -1,42 +1,48 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('copy') {{ trans('entities.pages_copy') }}</h3>
-            <div class="body">
-                <form action="{{ $page->getUrl('/copy') }}" method="POST">
-                    {!! csrf_field() !!}
-
-                    <div class="form-group title-input">
-                        <label for="name">{{ trans('common.name') }}</label>
-                        @include('form/text', ['name' => 'name'])
-                    </div>
 
-                    <div class="form-group" collapsible>
-                        <div class="collapse-title text-primary" collapsible-trigger>
-                            <label for="entity_selection">{{ trans('entities.pages_copy_desination') }}</label>
-                        </div>
-                        <div class="collapse-content" collapsible-content>
-                            @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter', 'entityPermission' => 'page-create'])
-                        </div>
-                    </div>
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $page->book,
+                $page->chapter,
+                $page,
+                $page->getUrl('/copy') => [
+                    'text' => trans('entities.pages_copy'),
+                    'icon' => 'copy',
+                ]
+            ]])
+        </div>
 
+        <div class="card content-wrap auto-height">
 
-                    <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_copy') }}</button>
+            <h1 class="list-heading">{{ trans('entities.pages_copy') }}</h1>
+
+            <form action="{{ $page->getUrl('/copy') }}" method="POST">
+                {!! csrf_field() !!}
+
+                <div class="form-group title-input">
+                    <label for="name">{{ trans('common.name') }}</label>
+                    @include('form.text', ['name' => 'name'])
+                </div>
+
+                <div class="form-group" collapsible>
+                    <div class="collapse-title text-primary" collapsible-trigger>
+                        <label for="entity_selection">{{ trans('entities.pages_copy_desination') }}</label>
                     </div>
-                </form>
-            </div>
+                    <div class="collapse-content" collapsible-content>
+                        @include('components.entity-selector', ['name' => 'entity_selection', 'selectorSize' => 'large', 'entityTypes' => 'book,chapter', 'entityPermission' => 'page-create'])
+                    </div>
+                </div>
+
+                <div class="form-group text-right">
+                    <a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button primary">{{ trans('entities.pages_copy') }}</button>
+                </div>
+            </form>
+
         </div>
     </div>
 
index 901ea182c3ee5886d656cc6d339da83594f6eb9c..a72157a83c45ef6a3dab3d7ab467dc786df2fe10 100644 (file)
@@ -1,28 +1,43 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ $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">
-                    <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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $page->book,
+                $page->chapter,
+                $page,
+                $page->getUrl('/delete') => [
+                    'text' => trans('entities.pages_delete'),
+                    'icon' => 'delete',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ $page->draft ? trans('entities.pages_delete_draft') : trans('entities.pages_delete') }}</h1>
+
+
+            <div class="grid half v-center">
+                <div>
+                    <p class="text-neg">
+                        <strong>
+                            {{ $page->draft ? trans('entities.pages_delete_draft_confirm'): trans('entities.pages_delete_confirm') }}
+                        </strong>
+                    </p>
+                </div>
+                <div>
+                    <form action="{{ $page->getUrl() }}" method="POST">
+                        {!! csrf_field() !!}
+                        <input type="hidden" name="_method" value="DELETE">
+                        <div class="form-group text-right">
+                            <a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                            <button type="submit" class="button primary">{{ trans('common.confirm') }}</button>
+                        </div>
+                    </form>
+                </div>
             </div>
         </div>
     </div>
index b4991d79d2f035f6a91a2d267b2fdc86248a2e87..eb2fab94cb3076602b909499ee5a29656c7fe257 100644 (file)
@@ -1,15 +1,17 @@
 @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">
+    <div class="container small pt-xl">
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ $title }}</h1>
+
+            <div class="book-contents">
+                @include('partials.entity-list', ['entities' => $pages, 'style' => 'detailed'])
+            </div>
+
+            <div class="text-center">
                 {!! $pages->links() !!}
             </div>
         </div>
-
     </div>
 @stop
\ No newline at end of file
index e818e996ec7e8d40d8765baffa1cf0d75e3b78f7..3dbb5d3bbae84e3560c1816e287db48e52a67b69 100644 (file)
@@ -13,8 +13,8 @@
             @if(!isset($isDraft))
                 <input type="hidden" name="_method" value="PUT">
             @endif
-            @include('pages/form', ['model' => $page])
-            @include('pages/form-toolbox')
+            @include('pages.form', ['model' => $page])
+            @include('pages.form-toolbox')
         </form>
     </div>
     
index 0f76a8861a964239bcdf1eb35086c62947db3c2f..e40643c256ff5346124a36dc717c5dada02fadde 100644 (file)
     @include('partials.custom-head')
 </head>
 <body>
-<div class="container" id="page-show">
-    <div class="row">
-        <div class="col-md-8 col-md-offset-2">
-            <div class="page-content">
 
-                @include('pages.page-display')
+<div id="page-show">
+    <div class="page-content">
 
-                <hr>
+        @include('pages.page-display')
 
-                <div class="text-muted text-small">
-                    @include('partials.entity-export-meta', ['entity' => $page])
-                </div>
+        <hr>
 
-            </div>
+        <div class="text-muted text-small">
+            @include('partials.entity-export-meta', ['entity' => $page])
         </div>
+
     </div>
 </div>
+
 </body>
 </html>
index 01c224543987c03f14a40abd73de7f0d62b1ad24..e515c0b2d0fc82df06bcaab3b172b98895f39d2b 100644 (file)
 
     <div toolbox-tab-content="tags">
         <h4>{{ trans('entities.page_tags') }}</h4>
-        <div class="padded">
+        <div class="px-l">
             @include('components.tag-manager', ['entity' => $page, 'entityType' => 'page'])
         </div>
     </div>
 
     @if(userCan('attachment-create-all'))
-        <div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id or 0 }}">
+        <div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id ?? 0 }}">
             <h4>{{ trans('entities.attachments') }}</h4>
-            <div class="padded files">
+            <div class="px-l files">
 
                 <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>
+                    <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
 
                     <div class="tab-container">
                         <div class="nav-tabs">
                             <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">@icon('grip')</div>
-                                    <div class="padded">
+                                    <div class="py-s">
                                         <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>
+                                            <span class="text-neg small">{{ trans('entities.attachments_delete_confirm') }}</span>
                                             <br>
                                             <span class="text-primary small" @click="file.deleting = false;">{{ trans('common.cancel') }}</span>
                                         </div>
@@ -46,7 +46,7 @@
                                     <div @click="deleteFile(file)" class="drag-card-action text-center text-neg">@icon('close')</div>
                                 </div>
                             </draggable>
-                            <p class="small muted" v-if="files.length === 0">
+                            <p class="small text-muted" v-if="files.length === 0">
                                 {{ trans('entities.attachments_no_files') }}
                             </p>
                         </div>
                             <dropzone placeholder="{{ trans('entities.attachments_dropzone') }}" :upload-url="getUploadUrl()" :uploaded-to="pageId" @success="uploadSuccess"></dropzone>
                         </div>
                         <div v-show="tab === 'link'" @keypress.enter.prevent="attachNewLink(file)">
-                            <p class="muted small">{{ trans('entities.attachments_explain_link') }}</p>
+                            <p class="text-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') }}" v-model="file.name">
-                                <p class="small neg" v-for="error in errors.link.name" v-text="error"></p>
+                                <p class="small text-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') }}" v-model="file.link">
-                                <p class="small neg" v-for="error in errors.link.link" v-text="error"></p>
+                                <p class="small text-neg" v-for="error in errors.link.link" v-text="error"></p>
                             </div>
-                            <button @click.prevent="attachNewLink(file)" class="button pos">{{ trans('entities.attach') }}</button>
+                            <button @click.prevent="attachNewLink(file)" class="button primary">{{ trans('entities.attach') }}</button>
 
                         </div>
                     </div>
@@ -78,7 +78,7 @@
                     <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') }}" v-model="fileToEdit.name">
-                        <p class="small neg" v-for="error in errors.edit.name" v-text="error"></p>
+                        <p class="small text-neg" v-for="error in errors.edit.name" v-text="error"></p>
                     </div>
 
                     <div class="tab-container">
                             <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') }}" v-model="fileToEdit.link">
-                                <p class="small neg" v-for="error in errors.edit.link" v-text="error"></p>
+                                <p class="small text-neg" v-for="error in errors.edit.link" v-text="error"></p>
                             </div>
                         </div>
                     </div>
 
                     <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>
+                    <button @click.enter.prevent="updateFile(fileToEdit)" class="button primary">{{ trans('common.save') }}</button>
                 </div>
 
             </div>
index 38e2eae39a4ea7d9eea7bed8a167d44313754fbc..7a530b0a97b942001aa9e1ef0005ec2c9394bef3 100644 (file)
@@ -3,56 +3,52 @@
      drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
      drawio-enabled="{{ config('services.drawio') ? 'true' : 'false' }}"
      editor-type="{{ setting('app-editor') }}"
-     page-id="{{ $model->id or 0 }}"
+     page-id="{{ $model->id ?? 0 }}"
      text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
-     page-new-draft="{{ $model->draft or 0 }}"
-     page-update-draft="{{ $model->isDraft or 0 }}">
+     page-new-draft="{{ $model->draft ?? 0 }}"
+     page-update-draft="{{ $model->isDraft ?? 0 }}">
 
     {{ csrf_field() }}
 
     {{--Header Bar--}}
-    <div class="faded-small toolbar">
-        <div class="container fluid">
-            <div class="row">
-                <div class="col-sm-4 faded">
-                    <div class="action-buttons text-left">
-                        <a href="{{ back()->getTargetUrl() }}" class="text-button text-primary">@icon('back'){{ trans('common.back') }}</a>
-                        <a onclick="$('body>header').slideToggle();" class="text-button text-primary">@icon('swap-vertical'){{ trans('entities.pages_edit_toggle_header') }}</a>
-                    </div>
-                </div>
-                <div class="col-sm-4 faded text-center">
-
-                    <div v-show="draftsEnabled" dropdown class="dropdown-container draft-display">
-                        <a dropdown-toggle class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</a>
-                        @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
-                        <ul>
-                            <li>
-                                <a @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</a>
-                            </li>
-                            <li v-if="isNewDraft">
-                                <a href="{{ $model->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('entities.pages_edit_delete_draft') }}</a>
-                            </li>
-                            <li v-if="isUpdateDraft">
-                                <a type="button" @click="discardDraft" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</a>
-                            </li>
-                        </ul>
-                    </div>
+    <div class="primary-background-light toolbar page-edit-toolbar">
+        <div class="grid third v-center">
+
+            <div class="action-buttons text-left px-m py-xs">
+                <a href="{{ back()->getTargetUrl() }}" class="text-button text-primary">@icon('back')<span class="hide-under-l">{{ trans('common.back') }}</span></a>
+                <a onclick="$('body>header').slideToggle();" class="text-button text-primary">@icon('swap-vertical')<span class="hide-under-l">{{ trans('entities.pages_edit_toggle_header') }}</span></a>
+            </div>
+
+            <div class="text-center px-m py-xs">
+                <div v-show="draftsEnabled" dropdown class="dropdown-container draft-display text">
+                    <a dropdown-toggle class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</a>
+                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
+                    <ul>
+                        <li>
+                            <a @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</a>
+                        </li>
+                        <li v-if="isNewDraft">
+                            <a href="{{ $model->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('entities.pages_edit_delete_draft') }}</a>
+                        </li>
+                        <li v-if="isUpdateDraft">
+                            <a type="button" @click="discardDraft" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</a>
+                        </li>
+                    </ul>
                 </div>
-                <div class="col-sm-4 faded">
-                    <div class="action-buttons" v-cloak>
-                        <div dropdown class="dropdown-container">
-                            <a dropdown-toggle class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></a>
-                            <ul class="wide">
-                                <li class="padded">
-                                    <p class="text-muted">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
-                                    <input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" v-model="changeSummary" />
-                                </li>
-                            </ul>
-                        </div>
+            </div>
 
-                        <button type="submit" id="save-button" class="text-button text-pos">@icon('save'){{ trans('entities.pages_save') }}</button>
-                    </div>
+            <div class="action-buttons px-m py-xs" v-cloak>
+                <div dropdown class="dropdown-container">
+                    <a dropdown-toggle class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></a>
+                    <ul class="wide">
+                        <li class="px-l py-m">
+                            <p class="text-muted pb-s">{{ trans('entities.pages_edit_enter_changelog_desc') }}</p>
+                            <input name="summary" id="summary-input" type="text" placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" v-model="changeSummary" />
+                        </li>
+                    </ul>
                 </div>
+
+                <button type="submit" id="save-button" class="float-left text-primary text-button text-pos-hover">@icon('save')<span>{{ trans('entities.pages_save') }}</span></button>
             </div>
         </div>
     </div>
@@ -60,7 +56,7 @@
     {{--Title input--}}
     <div class="title-input page-title clearfix" v-pre>
         <div class="input">
-            @include('form/text', ['name' => 'name', 'placeholder' => trans('entities.pages_title')])
+            @include('form.text', ['name' => 'name', 'placeholder' => trans('entities.pages_title')])
         </div>
     </div>
 
@@ -71,7 +67,7 @@
         @if(setting('app-editor') === 'wysiwyg')
             <div wysiwyg-editor class="flex-fill flex">
                 <textarea id="html-editor"  name="html" rows="5" v-pre
-                    @if($errors->has('html')) class="neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
+                    @if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{htmlspecialchars( old('html') ? old('html') : $model->html)}}@endif</textarea>
             </div>
 
             @if($errors->has('html'))
@@ -83,9 +79,9 @@
         @if(setting('app-editor') === 'markdown')
             <div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
 
-                <div class="markdown-editor-wrap">
+                <div class="markdown-editor-wrap active">
                     <div class="editor-toolbar">
-                        <span class="float left">{{ trans('entities.pages_md_editor') }}</span>
+                        <span class="float left editor-toolbar-label">{{ trans('entities.pages_md_editor') }}</span>
                         <div class="float right buttons">
                             @if(config('services.drawio'))
                                 <button class="text-button" type="button" data-action="insertDrawing">@icon('drawing'){{ trans('entities.pages_md_insert_drawing') }}</button>
 
                     <div markdown-input class="flex flex-fill">
                         <textarea  id="markdown-editor-input"  name="markdown" rows="5"
-                            @if($errors->has('markdown')) class="neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
+                            @if($errors->has('markdown')) class="text-neg" @endif>@if(isset($model) || old('markdown')){{htmlspecialchars( old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown))}}@endif</textarea>
                     </div>
 
                 </div>
 
                 <div class="markdown-editor-wrap">
                     <div class="editor-toolbar">
-                        <div class="">{{ trans('entities.pages_md_preview') }}</div>
+                        <div class="editor-toolbar-label">{{ trans('entities.pages_md_preview') }}</div>
                     </div>
                     <div class="markdown-display page-content">
                     </div>
index 2d059bf36fc194c7994d53e0333c7260b44ba84b..4650f3a1c1377808f6e09357a5c2523b37f01b13 100644 (file)
@@ -1,25 +1,37 @@
-@extends('base')
+@extends('simple-layout')
 
-@section('content')
+@section('body')
 
     <div class="container small">
-        <h1>{{ trans('entities.pages_new') }}</h1>
-        <form action="{{  $parent->getUrl('/create-guest-page') }}" method="POST">
 
-            {!! csrf_field() !!}
-
-            <div class="form-group title-input">
-                <label for="name">{{ trans('entities.pages_name') }}</label>
-                @include('form/text', ['name' => 'name'])
-            </div>
-
-            <div class="form-group">
-                <a href="{{ $parent->getUrl() }}" class="button muted">{{ trans('common.cancel') }}</a>
-                <button type="submit" class="button pos">{{ trans('common.continue') }}</button>
-            </div>
-
-        </form>
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                ($parent->isA('chapter') ? $parent->book : null),
+                $parent,
+                $parent->getUrl('/create-page') => [
+                    'text' => trans('entities.pages_new'),
+                    'icon' => 'add',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.pages_new') }}</h1>
+            <form action="{{  $parent->getUrl('/create-guest-page') }}" method="POST">
+                {!! csrf_field() !!}
+
+                <div class="form-group title-input">
+                    <label for="name">{{ trans('entities.pages_name') }}</label>
+                    @include('form.text', ['name' => 'name'])
+                </div>
+
+                <div class="form-group text-right">
+                    <a href="{{ $parent->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button primary">{{ trans('common.continue') }}</button>
+                </div>
+
+            </form>
+        </div>
     </div>
 
-
 @stop
\ No newline at end of file
index b13bb0f120211dda32f3f60fc1c7acaa5cda059f..1e26cf1d546227566cd2798cd2f305ebbfab8148 100644 (file)
@@ -1,44 +1,5 @@
-<div class="page {{$page->draft ? 'draft' : ''}} entity-list-item" data-entity-type="page" data-entity-id="{{$page->id}}">
-    <h4>
-        @if (isset($showPath) && $showPath)
-            <a href="{{ $page->book->getUrl() }}" class="text-book">
-                @icon('book'){{ $page->book->getShortName() }}
-            </a>
-            <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
-            @if($page->chapter)
-                <a href="{{ $page->chapter->getUrl() }}" class="text-chapter">
-                    @icon('chapter'){{ $page->chapter->getShortName() }}
-                </a>
-                <span class="text-muted">&nbsp;&nbsp;&raquo;&nbsp;&nbsp;</span>
-            @endif
-        @endif
-        <a href="{{ $page->getUrl() }}" class="text-page entity-list-item-link">@icon('page')<span class="entity-list-item-name break-text">{{ $page->name }}</span></a>
-    </h4>
-
+@component('partials.entity-list-item-basic', ['entity' => $page])
     <div class="entity-item-snippet">
-        @if(isset($page->searchSnippet))
-            <p class="text-muted break-text">{!! $page->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $page->getExcerpt() }}</p>
-        @endif
+        <p class="text-muted break-text">{{ $page->getExcerpt() }}</p>
     </div>
-
-    @if(isset($style) && $style === 'detailed')
-        <div class="row meta text-muted text-small">
-            <div class="col-md-6">
-                @include('partials.entity-meta', ['entity' => $page])
-            </div>
-            <div class="col-md-6">
-                <a class="text-book" href="{{ $page->book->getUrl() }}">@icon('book'){{ $page->book->getShortName(30) }}</a>
-                <br>
-                @if($page->chapter)
-                    <a class="text-chapter" href="{{ $page->chapter->getUrl() }}">@icon('chapter'){{ $page->chapter->getShortName(30) }}</a>
-                @else
-                    @icon('chapter') {{ trans('entities.pages_not_in_chapter') }}
-                @endif
-            </div>
-        </div>
-    @endif
-
-
-</div>
\ No newline at end of file
+@endcomponent
\ No newline at end of file
index 5a5c7e3f93c9b5ff76c7f1ebc9b04ca1870dcfb8..83421be934b4e169076285d8dc79721a7af23e51 100644 (file)
@@ -1,30 +1,36 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('folder') {{ 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', 'entityPermission' => 'page-create'])
-
-                    <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 class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $page->book,
+                $page->chapter,
+                $page,
+                $page->getUrl('/move') => [
+                    'text' => trans('entities.pages_move'),
+                    'icon' => 'folder',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.pages_move') }}</h1>
+
+            <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', 'entityPermission' => 'page-create'])
+
+                <div class="form-group text-right">
+                    <a href="{{ $page->getUrl() }}" class="button outline">{{ trans('common.cancel') }}</a>
+                    <button type="submit" class="button primary">{{ trans('entities.pages_move') }}</button>
+                </div>
+            </form>
+
         </div>
     </div>
 
diff --git a/resources/views/pages/permissions.blade.php b/resources/views/pages/permissions.blade.php
new file mode 100644 (file)
index 0000000..260f0e4
--- /dev/null
@@ -0,0 +1,25 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container">
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $page->book,
+                $page->chapter,
+                $page,
+                $page->getUrl('/permissions') => [
+                    'text' => trans('entities.pages_permissions'),
+                    'icon' => 'lock',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.pages_permissions') }}</h1>
+            @include('form.entity-permissions', ['model' => $page])
+        </div>
+    </div>
+
+@stop
diff --git a/resources/views/pages/restrictions.blade.php b/resources/views/pages/restrictions.blade.php
deleted file mode 100644 (file)
index a7a1e18..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-@extends('simple-layout')
-
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-@stop
-
-@section('body')
-    <div class="container">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('lock') {{ trans('entities.pages_permissions') }}</h3>
-            <div class="body">
-                @include('form.restriction-form', ['model' => $page])
-            </div>
-        </div>
-    </div>
-@stop
index f2d181fa16123c0e7b5b5cf00542e8bef221fc74..3ce5b349f1afc8c79645e3bcd16e500bc95e1ca3 100644 (file)
@@ -1,8 +1,8 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('sidebar')
-    <div class="card">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
+@section('left')
+    <div id="revision-details" class="entity-details mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
         <div class="body text-small text-muted">
             @include('partials.entity-meta', ['entity' => $revision])
         </div>
 
 @section('body')
 
-    <div class="container">
-        <div class="row">
-            <div class="col-md-9">
-                <div class="page-content page-revision">
-                    @include('pages.page-display')
-                </div>
-            </div>
-        </div>
+    <div class="mb-m">
+        @include('partials.breadcrumbs', ['crumbs' => [
+            $page->$book,
+            $page->chapter,
+            $page,
+            $page->getUrl('/revisions') => [
+                'text' => trans('entities.pages_revisions'),
+                'icon' => 'history',
+            ],
+            $revision->getUrl('/changes') => $diff ? trans('entities.pages_revisions_numbered_changes', ['id' => $revision->id]) : null,
+            $revision->getUrl() => !$diff ? trans('entities.pages_revisions_numbered', ['id' => $revision->id]) : null,
+        ]])
     </div>
 
-@stop
+    <div class="card content-wrap">
+        <div class="page-content page-revision">
+            @include('pages.page-display')
+        </div>
+    </div>
 
-@section('scripts')
-    <script>
-        setupPageShow(null);
-    </script>
 @stop
\ No newline at end of file
index 990b4fca2b8496bd3d62d43da55dff330331dbf8..e5515a7c9d20fe514a61bdccdaca4d89949bc05d 100644 (file)
@@ -1,76 +1,92 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
-    </div>
-@stop
-
 @section('body')
     <div class="container">
-        <p>&nbsp;</p>
 
-        <div class="card">
-            <h3>@icon('history') {{ trans('entities.pages_revisions') }}</h3>
-            <div class="body">
-                @if(count($page->revisions) > 0)
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $page->book,
+                $page->chapter,
+                $page,
+                $page->getUrl('/revisions') => [
+                    'text' => trans('entities.pages_revisions'),
+                    'icon' => 'history',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.pages_revisions') }}</h1>
+            @if(count($page->revisions) > 0)
 
-                    <table class="table">
+                <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>
-                            <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->formatLocalized('%e %B %Y %H:%M:%S') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
-                                <td>{{ $revision->summary }}</td>
-                                <td class="actions">
-                                    <a href="{{ $revision->getUrl('changes') }}" target="_blank">{{ trans('entities.pages_revisions_changes') }}</a>
-                                    <span class="text-muted">&nbsp;|&nbsp;</span>
+                            <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->formatLocalized('%e %B %Y %H:%M:%S') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
+                            <td>{{ $revision->summary }}</td>
+                            <td class="actions">
+                                <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>
-                                        <span class="text-muted">&nbsp;|&nbsp;</span>
-                                        <div dropdown class="dropdown-container">
-                                            <a dropdown-toggle>{{ trans('common.delete') }}</a>
-                                            <ul>
-                                                <li class="padded"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
-                                                <li>
-                                                    <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
-                                                        {!! csrf_field() !!}
-                                                        <input type="hidden" name="_method" value="DELETE">
-                                                        <button type="submit" class="text-button neg">@icon('delete'){{ trans('common.delete') }}</button>
-                                                    </form>
-                                                </li>
-                                            </ul>
-                                        </div>
-                                    @endif
-                                </td>
-                            </tr>
-                        @endforeach
-                    </table>
+                                @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') }}"></a>
+                                    <div dropdown class="dropdown-container">
+                                        <a dropdown-toggle>{{ trans('entities.pages_revisions_restore') }}</a>
+                                        <ul>
+                                            <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_restore_confirm')}}</small></li>
+                                            <li>
+                                                <form action="{{ $revision->getUrl('/restore') }}" method="POST">
+                                                    {!! csrf_field() !!}
+                                                    <input type="hidden" name="_method" value="PUT">
+                                                    <button type="submit" class="text-button text-primary">@icon('history'){{ trans('entities.pages_revisions_restore') }}</button>
+                                                </form>
+                                            </li>
+                                        </ul>
+                                    </div>
+                                    <span class="text-muted">&nbsp;|&nbsp;</span>
+                                    <div dropdown class="dropdown-container">
+                                        <a dropdown-toggle>{{ trans('common.delete') }}</a>
+                                        <ul>
+                                            <li class="px-m py-s"><small class="text-muted">{{trans('entities.revision_delete_confirm')}}</small></li>
+                                            <li>
+                                                <form action="{{ $revision->getUrl('/delete/') }}" method="POST">
+                                                    {!! csrf_field() !!}
+                                                    <input type="hidden" name="_method" value="DELETE">
+                                                    <button type="submit" class="text-button text-neg">@icon('delete'){{ trans('common.delete') }}</button>
+                                                </form>
+                                            </li>
+                                        </ul>
+                                    </div>
+                                @endif
+                            </td>
+                        </tr>
+                    @endforeach
+                </table>
 
-                @else
-                    <p>{{ trans('entities.pages_revisions_none') }}</p>
-                @endif
-            </div>
+            @else
+                <p>{{ trans('entities.pages_revisions_none') }}</p>
+            @endif
         </div>
 
     </div>
index db2f1462e3d05bedbea27fef66c9677c5efc599c..91a90cb00d8d9e5c26356f629624837542b12b09 100644 (file)
@@ -1,50 +1,45 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-sm-8 col-xs-5 faded">
-        @include('pages._breadcrumbs', ['page' => $page])
+@section('body')
+
+    <div class="mb-m">
+        @include('partials.breadcrumbs', ['crumbs' => [
+            $page->book,
+            $page->hasChapter() ? $page->chapter : null,
+            $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">@icon('export'){{ 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" >@icon('edit'){{ trans('common.edit') }}</a>
-            @endif
-            @if((userCan('page-view', $page) && userCanOnAny('page-create')) || 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">@icon('more') {{ trans('common.more') }}</a>
-                    <ul>
-                        @if(userCanOnAny('page-create'))
-                            <li><a href="{{ $page->getUrl('/copy') }}" class="text-primary" >@icon('copy'){{ trans('common.copy') }}</a></li>
-                        @endif
-                        @if(userCan('page-delete', $page) && userCan('page-update', $page))
-                            <li><a href="{{ $page->getUrl('/move') }}" class="text-primary" >@icon('folder'){{ trans('common.move') }}</a></li>
-                        @endif
-                        @if(userCan('page-update', $page))
-                            <li><a href="{{ $page->getUrl('/revisions') }}" class="text-primary">@icon('history'){{ trans('entities.revisions') }}</a></li>
-                        @endif
-                        @if(userCan('restrictions-manage', $page))
-                            <li><a href="{{ $page->getUrl('/permissions') }}" class="text-primary">@icon('lock'){{ trans('entities.permissions') }}</a></li>
-                        @endif
-                        @if(userCan('page-delete', $page))
-                            <li><a href="{{ $page->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('common.delete') }}</a></li>
-                        @endif
-                    </ul>
+
+    <div class="content-wrap card">
+        <div class="page-content flex" page-display="{{ $page->id }}">
+
+            <div class="pointer-container" id="pointer">
+                <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
+                    <span class="icon text-primary">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
+                    <span class="input-group">
+                    <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') }}">@icon('copy')</button>
+                </span>
+                    @if(userCan('page-update', $page))
+                        <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
+                           class="button icon heading-edit-icon" title="{{ trans('entities.pages_edit_content_link')}}">@icon('edit')</a>
+                    @endif
                 </div>
-            @endif
+            </div>
 
+            @include('pages.page-display')
         </div>
     </div>
+
+    @if ($commentsEnabled)
+        <div class="container small p-none comments-container mb-l">
+            @include('comments.comments', ['page' => $page])
+            <div class="clearfix"></div>
+        </div>
+    @endif
 @stop
 
-@section('sidebar')
+@section('left')
 
     @if($page->tags->count() > 0)
         <section>
     @endif
 
     @if ($page->attachments->count() > 0)
-        <div class="card">
-            <h3>@icon('attach') {{ trans('entities.pages_attachments') }}</h3>
+        <div id="page-attachments" class="mb-l">
+            <h5>{{ trans('entities.pages_attachments') }}</h5>
             <div class="body">
                 @foreach($page->attachments as $attachment)
-                    <div class="attachment">
-                        <a href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif>@icon($attachment->external ? 'export' : 'file'){{ $attachment->name }}</a>
+                    <div class="attachment icon-list">
+                        <a class="icon-list-item py-xs" href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif>
+                            <span class="icon">@icon($attachment->external ? 'export' : 'file')</span>
+                            <span>{{ $attachment->name }}</span>
+                        </a>
                     </div>
                 @endforeach
             </div>
     @endif
 
     @if (isset($pageNav) && count($pageNav))
-        <div class="card">
-            <h3>@icon('open-book') {{ trans('entities.pages_navigation') }}</h3>
+        <div id="page-navigation" class="mb-xl">
+            <h5>{{ trans('entities.pages_navigation') }}</h5>
             <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>
+                            <div class="primary-background sidebar-page-nav-bullet"></div>
                         </li>
                     @endforeach
                 </div>
         </div>
     @endif
 
-    <div class="card entity-details">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
-        <div class="body text-muted text-small blended-links">
+    @include('partials.book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
+@stop
+
+@section('right')
+    <div id="page-details" class="entity-details mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
+        <div class="body text-small blended-links">
             @include('partials.entity-meta', ['entity' => $page])
 
             @if($book->restricted)
         </div>
     </div>
 
-    @include('partials/book-tree', ['book' => $book, 'sidebarTree' => $sidebarTree])
+    <div class="actions mb-xl">
+        <h5>Actions</h5>
 
-@stop
+        <div class="icon-list text-primary">
 
-@section('body-wrap-classes', 'flex-fill columns')
-
-@section('body')
+            {{--User Actions--}}
+            @if(userCan('page-update', $page))
+                <a href="{{ $page->getUrl('/edit') }}" class="icon-list-item">
+                    <span>@icon('edit')</span>
+                    <span>{{ trans('common.edit') }}</span>
+                </a>
+            @endif
+            @if(userCanOnAny('page-create'))
+                <a href="{{ $page->getUrl('/copy') }}" class="icon-list-item">
+                    <span>@icon('copy')</span>
+                    <span>{{ trans('common.copy') }}</span>
+                </a>
+            @endif
+            @if(userCan('page-update', $page))
+                @if(userCan('page-delete', $page))
+                       <a href="{{ $page->getUrl('/move') }}" class="icon-list-item">
+                           <span>@icon('folder')</span>
+                           <span>{{ trans('common.move') }}</span>
+                       </a>
+                @endif
+                <a href="{{ $page->getUrl('/revisions') }}" class="icon-list-item">
+                    <span>@icon('history')</span>
+                    <span>{{ trans('entities.revisions') }}</span>
+                </a>
+            @endif
+            @if(userCan('restrictions-manage', $page))
+                <a href="{{ $page->getUrl('/permissions') }}" class="icon-list-item">
+                    <span>@icon('lock')</span>
+                    <span>{{ trans('entities.permissions') }}</span>
+                </a>
+            @endif
+            @if(userCan('page-delete', $page))
+                <a href="{{ $page->getUrl('/delete') }}" class="icon-list-item">
+                    <span>@icon('delete')</span>
+                    <span>{{ trans('common.delete') }}</span>
+                </a>
+            @endif
 
-    <div class="page-content flex" page-display="{{ $page->id }}">
+            <hr class="primary-background"/>
 
-        <div class="pointer-container" id="pointer">
-            <div class="pointer anim {{ userCan('page-update', $page) ? 'is-page-editable' : ''}}" >
-                <span class="icon text-primary">@icon('link') @icon('include', ['style' => 'display:none;'])</span>
-                <span class="input-group">
-                    <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') }}">@icon('copy')</button>
-                </span>
-                @if(userCan('page-update', $page))
-                    <a href="{{ $page->getUrl('/edit') }}" id="pointer-edit" data-edit-href="{{ $page->getUrl('/edit') }}"
-                        class="button icon heading-edit-icon" title="{{ trans('entities.pages_edit_content_link')}}">@icon('edit')</a>
-                @endif
+            {{--Export--}}
+            <div dropdown class="dropdown-container block">
+                <div dropdown-toggle class="icon-list-item">
+                    <span>@icon('export')</span>
+                    <span>{{ trans('entities.export') }}</span>
+                </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>
             </div>
         </div>
 
-        @include('pages/page-display')
     </div>
-
-    @if ($commentsEnabled)
-      <div class="container small nopad comments-container">
-          @include('comments/comments', ['page' => $page])
-      </div>
-    @endif
 @stop
diff --git a/resources/views/partials/_header-dropdown.blade.php b/resources/views/partials/_header-dropdown.blade.php
deleted file mode 100644 (file)
index 176e007..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<div class="dropdown-container" dropdown>
-    <span class="user-name" dropdown-toggle>
-        <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
-        <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
-    </span>
-    <ul>
-        <li>
-            <a href="{{ baseUrl("/user/{$currentUser->id}") }}" class="text-primary">@icon('user') {{ trans('common.view_profile') }}</a>
-        </li>
-        <li>
-            <a href="{{ baseUrl("/settings/users/{$currentUser->id}") }}" class="text-primary">@icon('edit') {{ trans('common.edit_profile') }}</a>
-        </li>
-        <li>
-            <a href="{{ baseUrl('/logout') }}" class="text-neg">@icon('logout') {{ trans('auth.logout') }}</a>
-        </li>
-    </ul>
-</div>
\ No newline at end of file
index 1dbfc9de81f1e00a705ff97d763b53f0e29e546f..39fb35fe2728c4c50c2ee5392414f9dfdee0a5c8 100644 (file)
@@ -1,13 +1,13 @@
 
 {{--Requires an Activity item with the name $activity passed in--}}
 
-@if($activity->user)
-    <div class="left">
-        <img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{ $activity->user->name }}">
-    </div>
-@endif
+<div>
+    @if($activity->user)
+    <img class="avatar" src="{{ $activity->user->getAvatar(30) }}" alt="{{ $activity->user->name }}">
+    @endif
+</div>
 
-<div class="right" v-pre>
+<div v-pre>
     @if($activity->user)
         <a href="{{ $activity->user->getProfileUrl() }}">{{ $activity->user->name }}</a>
     @else
index 45322a1e53169ecd65d1089679839f3fb183ae49..397a69d38cb8f3b471cad9acfaaa958faa0ec0ea 100644 (file)
@@ -3,7 +3,7 @@
     <div class="activity-list">
         @foreach($activity as $activityItem)
             <div class="activity-list-item">
-                @include('partials/activity-item', ['activity' => $activityItem])
+                @include('partials.activity-item', ['activity' => $activityItem])
             </div>
         @endforeach
     </div>
index 0f895cf59fed2d620f79d21b9fcad47fc4ce2306..73064dceb46b72b6d9f9f38b23c9aeb5e75d55af 100644 (file)
@@ -1,36 +1,30 @@
-<div class="card book-tree" v-pre>
-    <h3>@icon('book') {{ trans('entities.books_navigation') }}</h3>
-    <div class="body">
-        <ul class="sidebar-page-list menu">
+<div id="book-tree" class="book-tree mb-xl" v-pre>
+    <h5>{{ trans('entities.books_navigation') }}</h5>
 
-            @if (userCan('view', $book))
-                <li class="book-header"><a href="{{ $book->getUrl() }}" class="book {{ $current->matches($book)? 'selected' : '' }}">@icon('book'){{$book->name}}</a></li>
-            @endif
+    <ul class="sidebar-page-list mt-xs menu entity-list">
+        @if (userCan('view', $book))
+            <li class="list-item-book book">
+                @include('partials.entity-list-item-basic', ['entity' => $book, 'classes' => ($current->matches($book)? 'selected' : '')])
+            </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'))@icon('chapter')@else @icon('page')@endif{{ $bookChild->name }}
-                    </a>
+        @foreach($sidebarTree as $bookChild)
+            <li class="list-item-{{ $bookChild->getClassName() }} {{ $bookChild->getClassName() }} {{ $bookChild->isA('page') && $bookChild->draft ? 'draft' : '' }}">
+                @include('partials.entity-list-item-basic', ['entity' => $bookChild, 'classes' => $current->matches($bookChild)? 'selected' : ''])
 
-                    @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                        <p chapter-toggle class="text-muted @if($bookChild->matchesOrContains($current)) open @endif">
-                            @icon('caret-right') @icon('page') <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' : '' }}">
-                                        @icon('page') {{ $childPage->name }}
-                                    </a>
-                                </li>
-                            @endforeach
-                        </ul>
-                    @endif
+                @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
+                    <div class="entity-list-item no-hover">
+                        <span class="icon text-chapter">
 
+                        </span>
+                        <div class="content">
+                            @include('chapters.child-menu', ['chapter' => $bookChild, 'current' => $current])
+                        </div>
+                    </div>
 
-                </li>
-            @endforeach
-        </ul>
-    </div>
+                @endif
+
+            </li>
+        @endforeach
+    </ul>
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/breadcrumb-listing.blade.php b/resources/views/partials/breadcrumb-listing.blade.php
new file mode 100644 (file)
index 0000000..3dea320
--- /dev/null
@@ -0,0 +1,13 @@
+<div class="breadcrumb-listing" dropdown breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
+    <div class="breadcrumb-listing-toggle" dropdown-toggle>
+        <div class="separator">@icon('chevron-right')</div>
+    </div>
+    <div dropdown-menu class="breadcrumb-listing-dropdown card">
+        <div class="breadcrumb-listing-search">
+            @icon('search')
+            <input autocomplete="off" type="text" name="entity-search">
+        </div>
+        @include('partials.loading-icon')
+        <div class="breadcrumb-listing-entity-list px-m"></div>
+    </div>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/breadcrumbs.blade.php b/resources/views/partials/breadcrumbs.blade.php
new file mode 100644 (file)
index 0000000..df5d1aa
--- /dev/null
@@ -0,0 +1,54 @@
+<div class="breadcrumbs text-center">
+    <?php $breadcrumbCount = 0; ?>
+
+    {{-- Show top level books item --}}
+    @if (count($crumbs) > 0 && array_first($crumbs) instanceof  \BookStack\Entities\Book)
+        <a href="{{  baseUrl('/books')  }}" class="text-book icon-list-item outline-hover">
+            <span>@icon('books')</span>
+            <span>{{ trans('entities.books') }}</span>
+        </a>
+        <?php $breadcrumbCount++; ?>
+    @endif
+
+    {{-- Show top level shelves item --}}
+    @if (count($crumbs) > 0 && array_first($crumbs) instanceof  \BookStack\Entities\Bookshelf)
+        <a href="{{  baseUrl('/shelves')  }}" class="text-bookshelf icon-list-item outline-hover">
+            <span>@icon('bookshelf')</span>
+            <span>{{ trans('entities.shelves') }}</span>
+        </a>
+        <?php $breadcrumbCount++; ?>
+    @endif
+
+    @foreach($crumbs as $key => $crumb)
+        <?php $isEntity = ($crumb instanceof \BookStack\Entities\Entity); ?>
+
+        @if (is_null($crumb))
+            <?php continue; ?>
+        @endif
+        @if ($breadcrumbCount !== 0 && !$isEntity)
+            <div class="separator">@icon('chevron-right')</div>
+        @endif
+
+        @if (is_string($crumb))
+            <a href="{{  baseUrl($key)  }}">
+                {{ $crumb }}
+            </a>
+        @elseif (is_array($crumb))
+            <a href="{{  baseUrl($key)  }}" class="icon-list-item outline-hover">
+                <span>@icon($crumb['icon'])</span>
+                <span>{{ $crumb['text'] }}</span>
+            </a>
+        @elseif($isEntity && userCan('view', $crumb))
+            @if($breadcrumbCount > 0)
+                @include('partials.breadcrumb-listing', ['entity' => $crumb])
+            @endif
+            <a href="{{ $crumb->getUrl() }}" class="text-{{$crumb->getType()}} icon-list-item outline-hover">
+                <span>@icon($crumb->getType())</span>
+                <span>
+                    {{ $crumb->getShortName() }}
+                </span>
+            </a>
+        @endif
+        <?php $breadcrumbCount++; ?>
+    @endforeach
+</div>
\ No newline at end of file
index 0b9382f59be427f88b6c235c55ab075d87dce1e3..2a293edc5f4b5f22209d13860e038508978eee67 100644 (file)
@@ -1,21 +1,23 @@
 <style id="custom-styles" data-color="{{ setting('app-color') }}" data-color-light="{{ setting('app-color-light') }}">
-    header, [back-to-top], .primary-background {
+    .primary-background {
         background-color: {{ setting('app-color') }} !important;
     }
-    .faded-small, .primary-background-light {
+    .primary-background-light {
         background-color: {{ setting('app-color-light') }};
     }
-    .button-base, .button, input[type="button"], input[type="submit"] {
+    .button.primary, .button.primary:hover, .button.primary:active, .button.primary:focus {
         background-color: {{ setting('app-color') }};
         border-color: {{ setting('app-color') }};
     }
-    .button-base:hover, .button:hover, input[type="button"]:hover, input[type="submit"]:hover, .button:focus {
-        background-color: {{ setting('app-color') }};
-    }
     .nav-tabs a.selected, .nav-tabs .tab-item.selected {
         border-bottom-color: {{ setting('app-color') }};
     }
-    .text-primary, p.primary, p .primary, span.primary:hover, .text-primary:hover, a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus {
+    .text-primary, .text-primary-hover:hover, .text-primary:hover {
+        color: {{ setting('app-color') }} !important;
+        fill: {{ setting('app-color') }} !important;
+    }
+
+    a, a:hover, a:focus, .text-button, .text-button:hover, .text-button:focus {
         color: {{ setting('app-color') }};
         fill: {{ setting('app-color') }};
     }
diff --git a/resources/views/partials/entity-dashboard-search-box.blade.php b/resources/views/partials/entity-dashboard-search-box.blade.php
new file mode 100644 (file)
index 0000000..99d37c5
--- /dev/null
@@ -0,0 +1,7 @@
+<div class="mb-xl">
+    <form v-on:submit.prevent="searchBook" class="search-box flexible">
+        <input v-model="searchTerm" v-on:change="checkSearchForm" type="text" name="term" placeholder="{{ trans('entities.books_search_this') }}">
+        <button type="submit">@icon('search')</button>
+        <button v-if="searching" v-cloak class="search-box-cancel text-neg" v-on:click="clearSearch" type="button">@icon('close')</button>
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-dashboard-search-results.blade.php b/resources/views/partials/entity-dashboard-search-results.blade.php
new file mode 100644 (file)
index 0000000..68c6f53
--- /dev/null
@@ -0,0 +1,15 @@
+<div class="search-results" v-cloak v-show="searching">
+    <div class="grid half v-center">
+        <h3 class="text-muted px-none">
+            {{ trans('entities.search_results') }}
+        </h3>
+        <div class="text-right">
+            <a v-if="searching" v-on:click="clearSearch" class="button outline">{{ trans('entities.search_clear') }}</a>
+        </div>
+    </div>
+
+    <div v-if="!searchResults">
+        @include('partials.loading-icon')
+    </div>
+    <div class="book-contents" v-html="searchResults"></div>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-list-basic.blade.php b/resources/views/partials/entity-list-basic.blade.php
new file mode 100644 (file)
index 0000000..dc5c3f3
--- /dev/null
@@ -0,0 +1,11 @@
+<div class="entity-list {{ $style ?? '' }}">
+    @if(count($entities) > 0)
+        @foreach($entities as $index => $entity)
+            @include('partials.entity-list-item-basic', ['entity' => $entity])
+        @endforeach
+    @else
+        <p class="text-muted empty-text">
+            {{ $emptyText ?? trans('common.no_items') }}
+        </p>
+    @endif
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-list-item-basic.blade.php b/resources/views/partials/entity-list-item-basic.blade.php
new file mode 100644 (file)
index 0000000..c4942c7
--- /dev/null
@@ -0,0 +1,8 @@
+<?php $type = $entity->getType(); ?>
+<a href="{{ $entity->getUrl() }}" class="{{$type}} {{$type === 'page' && $entity->draft ? 'draft' : ''}} {{$classes ?? ''}} entity-list-item" data-entity-type="{{$type}}" data-entity-id="{{$entity->id}}">
+    <span class="icon text-{{$type}}">@icon($type)</span>
+    <div class="content">
+            <h4 class="entity-list-item-name break-text">{{ $entity->name }}</h4>
+            {{ $slot ?? '' }}
+    </div>
+</a>
\ No newline at end of file
diff --git a/resources/views/partials/entity-list-item.blade.php b/resources/views/partials/entity-list-item.blade.php
new file mode 100644 (file)
index 0000000..d42b196
--- /dev/null
@@ -0,0 +1,15 @@
+@component('partials.entity-list-item-basic', ['entity' => $entity])
+<div class="entity-item-snippet">
+
+    @if($showPath ?? false)
+        @if($entity->book_id)
+            <span class="text-book">{{ $entity->book->getShortName(42) }}</span>
+            @if($entity->chapter_id)
+                <span class="text-muted entity-list-item-path-sep">@icon('chevron-right')</span> <span class="text-chapter">{{ $entity->chapter->getShortName(42) }}</span>
+            @endif
+        @endif
+    @endif
+
+    <p class="text-muted break-text">{{ $entity->getExcerpt() }}</p>
+</div>
+@endcomponent
\ No newline at end of file
index 371f38d71c6b5e0c7a9de5a09d1c5e61f7ec38cc..c0f922fdda3a4b6dc3ece00f7e1aec7bf31d1f8d 100644 (file)
@@ -1,25 +1,12 @@
 
-<div class="entity-list @if(isset($style)){{ $style }}@endif">
+<div class="entity-list {{ $style ?? '' }}">
     @if(count($entities) > 0)
         @foreach($entities as $index => $entity)
-            @if($entity->isA('page'))
-                @include('pages/list-item', ['page' => $entity])
-            @elseif($entity->isA('book'))
-                @include('books/list-item', ['book' => $entity])
-            @elseif($entity->isA('chapter'))
-                @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true])
-            @elseif($entity->isA('bookshelf'))
-                @include('shelves/list-item', ['bookshelf' => $entity])
-            @endif
-
-            @if($index !== count($entities) - 1)
-                <hr>
-            @endif
-
+            @include('partials.entity-list-item', ['entity' => $entity, 'showPath' => $showPath ?? false])
         @endforeach
     @else
         <p class="text-muted empty-text">
-            {{ $emptyText or trans('common.no_items') }}
+            {{ $emptyText ?? trans('common.no_items') }}
         </p>
     @endif
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/sort.blade.php b/resources/views/partials/sort.blade.php
new file mode 100644 (file)
index 0000000..d7ed4d9
--- /dev/null
@@ -0,0 +1,32 @@
+<?php
+    $selectedSort = (isset($sort) && array_key_exists($sort, $options)) ? $sort : array_keys($options)[0];
+    $order = (isset($order) && in_array($order, ['asc', 'desc'])) ? $order : 'asc';
+?>
+<div class="list-sort-container" list-sort-control>
+    <div class="list-sort-label">{{ trans('common.sort') }}</div>
+    <form action="{{ baseUrl("/settings/users/{$currentUser->id}/change-sort/{$type}") }}" method="post">
+
+        {!! csrf_field() !!}
+        {!! method_field('PATCH') !!}
+        <input type="hidden" value="{{ $selectedSort }}" name="sort">
+        <input type="hidden" value="{{ $order }}" name="order">
+
+        <div class="list-sort">
+            <div class="list-sort-type dropdown-container" dropdown>
+                <div dropdown-toggle>{{ $options[$selectedSort] }}</div>
+                <ul>
+                    @foreach($options as $key => $label)
+                        <li @if($key === $selectedSort) class="active" @endif><a href="#" data-sort-value="{{$key}}">{{ $label }}</a></li>
+                    @endforeach
+                </ul>
+            </div>
+            <div class="list-sort-dir" data-sort-dir>
+                @if($order === 'desc')
+                    @icon('sort-up')
+                @else
+                    @icon('sort-down')
+                @endif
+            </div>
+        </div>
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/view-toggle.blade.php b/resources/views/partials/view-toggle.blade.php
new file mode 100644 (file)
index 0000000..9eb00e1
--- /dev/null
@@ -0,0 +1,18 @@
+<div>
+    <form action="{{ baseUrl("/settings/users/{$currentUser->id}/switch-${type}-view") }}" method="POST" class="inline">
+        {!! csrf_field() !!}
+        {!! method_field('PATCH') !!}
+        <input type="hidden" value="{{ $view === 'list'? 'grid' : 'list' }}" name="view_type">
+        @if ($view === 'list')
+            <button type="submit" class="icon-list-item text-primary">
+                <span class="icon">@icon('grid')</span>
+                <span>{{ trans('common.grid_view') }}</span>
+            </button>
+        @else
+            <button type="submit" class="icon-list-item text-primary">
+                <span>@icon('list')</span>
+                <span>{{ trans('common.list_view') }}</span>
+            </button>
+        @endif
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/public.blade.php b/resources/views/public.blade.php
deleted file mode 100644 (file)
index f6135cd..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-<!DOCTYPE html>
-<html class="shaded">
-<head>
-    <title>{{ setting('app-name') }}</title>
-
-    <!-- Meta -->
-    <meta name="viewport" content="width=device-width">
-    <meta name="token" content="{{ csrf_token() }}">
-    <meta name="base-url" content="{{ baseUrl('/') }}">
-    <meta charset="utf-8">
-
-    <!-- Styles and Fonts -->
-    <link rel="stylesheet" href="{{ versioned_asset('dist/styles.css') }}">
-    <link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
-
-    <!-- Scripts -->
-    @include('partials/custom-styles')
-
-    <!-- Custom user content -->
-    @if(setting('app-custom-head'))
-        {!! setting('app-custom-head') !!}
-    @endif
-</head>
-<body class="@yield('body-class')">
-
-@include('partials.notifications')
-
-<header id="header">
-    <div class="container fluid">
-        <div class="row">
-            <div class="col-sm-6">
-
-                <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
-                    @if (setting('app-name-header'))
-                        <span class="logo-text">{{ setting('app-name') }}</span>
-                    @endif
-                </a>
-            </div>
-            <div class="col-sm-6">
-                <div class="float right">
-                    <div class="links text-center">
-                        @yield('header-buttons')
-                    </div>
-                    @if(isset($signedIn) && $signedIn)
-                        @include('partials._header-dropdown', ['currentUser' => $currentUser])
-                    @endif
-                </div>
-            </div>
-        </div>
-    </div>
-</header>
-
-<section class="container">
-    @yield('content')
-</section>
-
-<script src="{{ versioned_asset('dist/app.js') }}"></script>
-</body>
-</html>
index a952079d566453b3893603cbfc81fd39ddce4103..fba67abf9e20c5866e5a1d4413d26bc03c43960b 100644 (file)
-@extends('sidebar-layout')
+@extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        <div class="breadcrumbs">
-            <a href="{{ baseUrl("/search?term=" . urlencode($searchTerm)) }}" class="text-button">@icon('search'){{ trans('entities.search_for_term', ['term' => $searchTerm]) }}</a>
-        </div>
-    </div>
-@stop
-
-@section('container-attrs')
-    id="search-system"
-@stop
+@section('body')
+    <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
 
-@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>
-                    <br>
-                    <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
-                    <label class="inline checkbox text-bookshelf"><input type="checkbox" v-on:change="typeChange" v-model="search.type.bookshelf" value="bookshelf">{{ trans('entities.shelf') }}</label>
-                </div>
+    <div class="container" id="search-system">
 
-                <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)">
-                                @icon('close')
-                            </button>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td colspan="2">
-                            <button type="button" class="text-button" v-on:click="addExact">
-                                @icon('add-circle'){{ 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)">
-                                @icon('close')
-                            </button>
-                        </td>
-                    </tr>
-                    <tr>
-                        <td colspan="2">
-                            <button type="button" class="text-button" v-on:click="addTag">
-                                @icon('add-circle'){{ trans('common.add') }}
-                            </button>
-                        </td>
-                    </tr>
-                </table>
-
-               @if(signedInUser())
-                    <h6 class="text-muted">{{ trans('entities.search_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>
-                @endif
-
-                <h6 class="text-muted">{{ trans('entities.search_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')">
-                                @icon('close')
-                            </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')">
-                                @icon('close')
-                            </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')">
-                                @icon('close')
-                            </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')">
-                                @icon('close')
-                            </button>
-                        </td>
-                    </tr>
-                </table>
-
-
-                <button type="submit" class="button primary">{{ trans('entities.search_update') }}</button>
-            </form>
+        <div class="my-s">
+            &nbsp;
         </div>
 
-    </div>
-@stop
-
-@section('body')
+        <div class="grid right-focus reverse-collapse gap-xl">
+            <div>
+                <div>
+                    <h5>{{ trans('entities.search_filters') }}</h5>
+
+                    <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>
+                            <br>
+                            <label class="inline checkbox text-book"><input type="checkbox" v-on:change="typeChange" v-model="search.type.book" value="book">{{ trans('entities.book') }}</label>
+                            <label class="inline checkbox text-bookshelf"><input type="checkbox" v-on:change="typeChange" v-model="search.type.bookshelf" value="bookshelf">{{ trans('entities.shelf') }}</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)">
+                                        @icon('close')
+                                    </button>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td colspan="2">
+                                    <button type="button" class="text-button" v-on:click="addExact">
+                                        @icon('add-circle'){{ 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)">
+                                        @icon('close')
+                                    </button>
+                                </td>
+                            </tr>
+                            <tr>
+                                <td colspan="2">
+                                    <button type="button" class="text-button" v-on:click="addTag">
+                                        @icon('add-circle'){{ trans('common.add') }}
+                                    </button>
+                                </td>
+                            </tr>
+                        </table>
+
+                        @if(signedInUser())
+                            <h6 class="text-muted">{{ trans('entities.search_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>
+                        @endif
+
+                        <h6 class="text-muted">{{ trans('entities.search_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')">
+                                        @icon('close')
+                                    </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')">
+                                        @icon('close')
+                                    </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')">
+                                        @icon('close')
+                                    </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')">
+                                        @icon('close')
+                                    </button>
+                                </td>
+                            </tr>
+                        </table>
+
+
+                        <button type="submit" class="button primary">{{ trans('entities.search_update') }}</button>
+                    </form>
 
-    <div class="container small" v-pre>
-        <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
+                </div>
+            </div>
+            <div>
+                <div v-pre class="card content-wrap">
+                    <h1 class="list-heading">{{ trans('entities.search_results') }}</h1>
+                    <h6 class="text-muted">{{ trans_choice('entities.search_total_results_found', $totalResults, ['count' => $totalResults]) }}</h6>
+                    <div class="book-contents">
+                        @include('partials.entity-list', ['entities' => $entities, 'showPath' => true])
+                    </div>
+                    @if($hasNextPage)
+                        <div class="text-right mt-m">
+                            <a href="{{ $nextPageLink }}" class="button outline">{{ trans('entities.search_more') }}</a>
+                        </div>
+                    @endif
+                </div>
+            </div>
+        </div>
 
-        <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
index 0b9119cd51584bad7dd1061621051b9ea430655c..36732c2b81bd37964256b94ca0b3ff968f722f28 100644 (file)
@@ -2,7 +2,7 @@
     @if(count($pages) > 0)
         @foreach($pages as $pageIndex => $page)
             <div class="anim searchResult" style="animation-delay: {{$pageIndex*50 . 'ms'}};">
-                @include('pages/list-item', ['page' => $page])
+                @include('pages.list-item', ['page' => $page])
                 <hr>
             </div>
         @endforeach
@@ -15,7 +15,7 @@
     <div class="page-list">
         @foreach($chapters as $chapterIndex => $chapter)
             <div class="anim searchResult" style="animation-delay: {{($chapterIndex+count($pages))*50 . 'ms'}};">
-                @include('chapters/list-item', ['chapter' => $chapter, 'hidePages' => true])
+                @include('chapters.list-item', ['chapter' => $chapter, 'hidePages' => true])
                 <hr>
             </div>
         @endforeach
index 1e0b325f9772c85a129ab2b2e0a73b0e91678414..36a28b93eb17fe910da3e5e4bd610ec5428f7984 100644 (file)
@@ -1,21 +1,15 @@
-<div class="entity-list @if(isset($style)){{ $style }}@endif">
+<div class="entity-list">
     @if(count($entities) > 0)
         @foreach($entities as $index => $entity)
-            @if($entity->isA('page'))
-                @include('pages/list-item', ['page' => $entity, 'showPath' => true])
-            @elseif($entity->isA('book'))
-                @include('books/list-item', ['book' => $entity])
-            @elseif($entity->isA('chapter'))
-                @include('chapters/list-item', ['chapter' => $entity, 'hidePages' => true, 'showPath' => true])
-            @endif
 
+            @include('partials.entity-list-item', ['entity' => $entity, 'showPath' => true])
             @if($index !== count($entities) - 1)
                 <hr>
             @endif
 
         @endforeach
     @else
-        <p class="text-muted">
+        <p class="text-muted text-large p-xl">
             {{ trans('common.no_items') }}
         </p>
     @endif
diff --git a/resources/views/search/entity-search-list.blade.php b/resources/views/search/entity-search-list.blade.php
deleted file mode 100644 (file)
index b60ca6f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-@extends('base')
-
-@section('content')
-
-    <div class="faded-small toolbar">
-        <div class="container">
-            <div class="row">
-                <div class="col-sm-12 faded">
-                    <div class="breadcrumbs">
-                        <a href="{{ baseUrl("/search/all?term={$searchTerm}") }}" class="text-button">@icon('search'){{ $searchTerm }}</a>
-                    </div>
-                </div>
-            </div>
-        </div>
-    </div>
-
-    <div class="container">
-        <div class="row">
-
-            <div class="col-sm-7">
-                <h1>{{ $title }}</h1>
-                @include('partials.entity-list', ['entities' => $entities, 'style' => 'detailed'])
-                {!! $entities->links() !!}
-            </div>
-
-        </div>
-    </div>
-@stop
\ No newline at end of file
index 26b81899e8bdb775c43ae9e6bd5dd1a576f66c9e..582bb078e092eeb8d75f3e00075ca5c424feae8a 100644 (file)
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'settings'])
-@stop
-
 @section('body')
-<div class="container small">
+    <div class="container small">
 
-    <div class="text-right text-muted container">
-        <br>
-        BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
-    </div>
+        <div class="grid left-focus v-center no-row-gap">
+            <div class="py-m">
+                @include('settings.navbar', ['selected' => 'settings'])
+            </div>
+            <div class="text-right mb-l px-m">
+                <br>
+                BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
+            </div>
+        </div>
 
-    <div class="card">
-        <h3>@icon('settings') {{ trans('settings.app_settings') }}</h3>
-        <div class="body">
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.app_features_security') }}</h2>
             <form action="{{ baseUrl("/settings") }}" method="POST">
-            {!! csrf_field() !!}
-                <div class="row">
+                {!! csrf_field() !!}
 
-                    <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 class="setting-list">
+
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label for="setting-app-public" class="setting-list-label">{{ trans('settings.app_public_access') }}</label>
+                            <p class="small">{!! trans('settings.app_public_access_desc') !!}</p>
+                            @if(userCan('users-manage'))
+                                <p class="small mb-none">
+                                    <a href="{{ baseUrl($guestUser->getEditUrl()) }}">{!! trans('settings.app_public_access_desc_guest') !!}</a>
+                                </p>
+                            @endif
                         </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>
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-app-public',
+                                'value' => setting('app-public'),
+                                'label' => trans('settings.app_public_access_toggle'),
+                            ])
                         </div>
-                        <div class="form-group">
-                            <label>{{ trans('settings.app_secure_images') }}</label>
+                    </div>
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-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>{{ trans('settings.app_disable_comments') }}</label>
-                            <p class="small">{{ trans('settings.app_disable_comments_desc') }}</p>
-                            @include('components.toggle-switch', ['name' => 'setting-app-disable-comments', 'value' => setting('app-disable-comments')])
+                        <div>
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-app-secure-images',
+                                'value' => setting('app-secure-images'),
+                                'label' => trans('settings.app_secure_images_toggle'),
+                            ])
+                        </div>
+                    </div>
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.app_disable_comments') }}</label>
+                            <p class="small">{!! trans('settings.app_disable_comments_desc') !!}</p>
+                        </div>
+                        <div>
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-app-disable-comments',
+                                'value' => setting('app-disable-comments'),
+                                'label' => trans('settings.app_disable_comments_toggle'),
+                            ])
+                        </div>
+                    </div>
+
+
+                </div>
+
+                <div class="form-group text-right">
+                    <button type="submit" class="button primary">{{ trans('settings.settings_save') }}</button>
+                </div>
+            </form>
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.app_customization') }}</h2>
+            <form action="{{ baseUrl("/settings") }}" method="POST">
+                {!! csrf_field() !!}
+
+                <div class="setting-list">
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label for="setting-app-name" class="setting-list-label">{{ trans('settings.app_name') }}</label>
+                            <p class="small">{{ trans('settings.app_name_desc') }}</p>
+                        </div>
+                        <div>
+                            <input type="text" value="{{ setting('app-name', 'BookStack') }}" name="setting-app-name" id="setting-app-name">
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-app-name-header',
+                                'value' => setting('app-name-header'),
+                                'label' => trans('settings.app_name_header'),
+                            ])
                         </div>
-                        <div class="form-group">
-                            <label for="setting-app-editor">{{ trans('settings.app_editor') }}</label>
+                    </div>
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.app_editor') }}</label>
                             <p class="small">{{ trans('settings.app_editor_desc') }}</p>
+                        </div>
+                        <div>
                             <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>
                         </div>
                     </div>
 
-                    <div class="col-md-6">
-                        <div class="form-group" id="logo-control">
-                            <label for="setting-app-logo">{{ trans('settings.app_logo') }}</label>
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.app_logo') }}</label>
                             <p class="small">{!! trans('settings.app_logo_desc') !!}</p>
-
+                        </div>
+                        <div>
                             @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
-                            ])
-
+                                     '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>
+                    </div>
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.app_primary_color') }}</label>
                             <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
+                        </div>
+                        <div>
                             <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 homepage-control 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>
+                    </div>
 
+                    <div homepage-control id="homepage-control" class="grid half gap-xl">
+                        <div>
+                            <label for="setting-app-homepage" class="setting-list-label">{{ trans('settings.app_homepage') }}</label>
+                            <p class="small">{{ trans('settings.app_homepage_desc') }}</p>
+                        </div>
+                        <div>
                             <select name="setting-app-homepage-type" id="setting-app-homepage-type">
                                 <option @if(setting('app-homepage-type') === 'default') selected @endif value="default">{{ trans('common.default') }}</option>
                                 <option @if(setting('app-homepage-type') === 'books') selected @endif value="books">{{ trans('entities.books') }}</option>
                                 <option @if(setting('app-homepage-type') === 'page') selected @endif value="page">{{ trans('entities.pages_specific') }}</option>
                             </select>
 
-                            <br><br>
-
-                            <div page-picker-container style="display: none;">
+                            <div page-picker-container style="display: none;" class="mt-m">
                                 @include('components.page-picker', ['name' => 'setting-app-homepage', 'placeholder' => trans('settings.app_homepage_select'), 'value' => setting('app-homepage')])
                             </div>
                         </div>
                     </div>
 
-                </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 class="simple-code-input" name="setting-app-custom-head" id="setting-app-custom-head">{{ setting('app-custom-head', '') }}</textarea>
+                    <div>
+                        <label for="setting-app-custom-head" class="setting-list-label">{{ 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" class="simple-code-input mt-m">{{ setting('app-custom-head', '') }}</textarea>
+                    </div>
+
+
                 </div>
 
                 <div class="form-group text-right">
-                    <button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
+                    <button type="submit" class="button primary">{{ trans('settings.settings_save') }}</button>
                 </div>
             </form>
         </div>
-    </div>
-
-    <p>&nbsp;</p>
 
-    <div class="card">
-        <h3>@icon('users-add') {{ trans('settings.reg_settings') }}</h3>
-        <div class="body">
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('settings.reg_settings') }}</h2>
             <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 class="setting-list">
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.reg_enable') }}</label>
+                            <p class="small">{!! trans('settings.reg_enable_desc') !!}</p>
                         </div>
-                        <div class="form-group">
+                        <div>
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-registration-enabled',
+                                'value' => setting('registration-enabled'),
+                                'label' => trans('settings.reg_enable_toggle')
+                            ])
+
                             <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\Auth\Role::all() as $role)
                                 @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>
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label for="setting-registration-restrict" class="setting-list-label">{{ trans('settings.reg_confirm_restrict_domain') }}</label>
                             <p class="small">{!! trans('settings.reg_confirm_restrict_domain_desc') !!}</p>
+                        </div>
+                        <div>
                             <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 class="grid half gap-xl">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.reg_email_confirmation') }}</label>
+                            <p class="small">{{ trans('settings.reg_confirm_email_desc') }}</p>
+                        </div>
+                        <div>
+                            @include('components.toggle-switch', [
+                                'name' => 'setting-registration-confirmation',
+                                'value' => setting('registration-confirmation'),
+                                'label' => trans('settings.reg_email_confirmation_toggle')
+                            ])
+                        </div>
+                    </div>
+
                 </div>
 
                 <div class="form-group text-right">
-                    <button type="submit" class="button pos">{{ trans('settings.settings_save') }}</button>
+                    <button type="submit" class="button primary">{{ trans('settings.settings_save') }}</button>
                 </div>
             </form>
         </div>
-    </div>
-
 
-</div>
-
-@include('components.image-manager', ['imageType' => 'system'])
-@include('components.entity-selector-popup', ['entityTypes' => 'page'])
+    </div>
 
+    @include('components.image-manager', ['imageType' => 'system'])
+    @include('components.entity-selector-popup', ['entityTypes' => 'page'])
 @stop
 
 @section('scripts')
         $('#setting-app-color').colorPicker({
             opacity: false,
             renderCallback: function($elm, toggled) {
-                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(',') +')';
+                const hexVal = '#' + this.color.colors.HEX;
+                const rgb = this.color.colors.RND.rgb;
+                const 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;
+                const isEmpty = $.trim($elm.val()).length === 0;
                 if (!isEmpty) $elm.val(hexVal);
                 $('#setting-app-color-light').val(isEmpty ? '' : rgbLightVal);
 
-                var customStyles = document.getElementById('custom-styles');
-                var oldColor = customStyles.getAttribute('data-color');
-                var oldColorLight = customStyles.getAttribute('data-color-light');
+                const customStyles = document.getElementById('custom-styles');
+                const oldColor = customStyles.getAttribute('data-color');
+                const oldColorLight = customStyles.getAttribute('data-color-light');
 
                 customStyles.innerHTML = customStyles.innerHTML.split(oldColor).join(hexVal);
                 customStyles.innerHTML = customStyles.innerHTML.split(oldColorLight).join(rgbLightVal);
index abf793adec9ea6640bcb9a93f671e9178eca5af6..c3ca8c96fbf95102e0a88de8422036fdacfff3cf 100644 (file)
@@ -1,46 +1,45 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'maintenance'])
-@stop
-
 @section('body')
 <div class="container small">
 
-    <div class="text-right text-muted container">
-        <br>
-        BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
+    <div class="grid left-focus v-center no-row-gap">
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'maintenance'])
+        </div>
+        <div class="text-right mb-l px-m">
+            <br>
+            BookStack @if(strpos($version, 'v') !== 0) version @endif {{ $version }}
+        </div>
     </div>
 
 
-    <div class="card" id="image-cleanup">
-        <h3>@icon('images') {{ trans('settings.maint_image_cleanup') }}</h3>
-        <div class="body">
-            <div class="row">
-                <div class="col-sm-6">
-                    <p class="small muted">{{ trans('settings.maint_image_cleanup_desc') }}</p>
-                </div>
-                <div class="col-sm-6">
-                    <form method="POST" action="{{ baseUrl('/settings/maintenance/cleanup-images') }}">
-                        {!! csrf_field()  !!}
-                        <input type="hidden" name="_method" value="DELETE">
-                        <div>
-                            @if(session()->has('cleanup-images-warning'))
-                                <p class="text neg">
-                                    {{ session()->get('cleanup-images-warning') }}
-                                </p>
-                                <input type="hidden" name="ignore_revisions" value="{{ session()->getOldInput('ignore_revisions', 'false') }}">
-                                <input type="hidden" name="confirm" value="true">
-                            @else
-                                <label>
-                                    <input type="checkbox" name="ignore_revisions" value="true">
-                                    {{ trans('settings.maint_image_cleanup_ignore_revisions') }}
-                                </label>
-                            @endif
-                        </div>
-                        <button class="button outline">{{ trans('settings.maint_image_cleanup_run') }}</button>
-                    </form>
-                </div>
+    <div id="image-cleanup" class="card content-wrap auto-height">
+        <h2 class="list-heading">{{ trans('settings.maint_image_cleanup') }}</h2>
+        <div class="grid half gap-xl">
+            <div>
+                <p class="small text-muted">{{ trans('settings.maint_image_cleanup_desc') }}</p>
+            </div>
+            <div>
+                <form method="POST" action="{{ baseUrl('/settings/maintenance/cleanup-images') }}">
+                    {!! csrf_field()  !!}
+                    <input type="hidden" name="_method" value="DELETE">
+                    <div>
+                        @if(session()->has('cleanup-images-warning'))
+                            <p class="text-neg">
+                                {{ session()->get('cleanup-images-warning') }}
+                            </p>
+                            <input type="hidden" name="ignore_revisions" value="{{ session()->getOldInput('ignore_revisions', 'false') }}">
+                            <input type="hidden" name="confirm" value="true">
+                        @else
+                            <label>
+                                <input type="checkbox" name="ignore_revisions" value="true">
+                                {{ trans('settings.maint_image_cleanup_ignore_revisions') }}
+                            </label>
+                        @endif
+                    </div>
+                    <button class="button outline">{{ trans('settings.maint_image_cleanup_run') }}</button>
+                </form>
             </div>
         </div>
     </div>
index f9db96894cf0b19bcb0d93b41b9b270697956f55..ddbaa3f2a865db53fea0cfd47c0ce1d33133f9dc 100644 (file)
@@ -1,13 +1,13 @@
 
-<div class="col-md-12 setting-nav nav-tabs">
+<div class="active-link-list">
     @if($currentUser->can('settings-manage'))
-        <a href="{{ baseUrl('/settings') }}" @if($selected == 'settings') class="selected text-button" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
-        <a href="{{ baseUrl('/settings/maintenance') }}" @if($selected == 'maintenance') class="selected text-button" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
+        <a href="{{ baseUrl('/settings') }}" @if($selected == 'settings') class="active" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
+        <a href="{{ baseUrl('/settings/maintenance') }}" @if($selected == 'maintenance') class="active" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
     @endif
     @if($currentUser->can('users-manage'))
-        <a href="{{ baseUrl('/settings/users') }}" @if($selected == 'users') class="selected text-button" @endif>@icon('users'){{ trans('settings.users') }}</a>
+        <a href="{{ baseUrl('/settings/users') }}" @if($selected == 'users') class="active" @endif>@icon('users'){{ trans('settings.users') }}</a>
     @endif
     @if($currentUser->can('user-roles-manage'))
-        <a href="{{ baseUrl('/settings/roles') }}" @if($selected == 'roles') class="selected text-button" @endif>@icon('lock-open'){{ trans('settings.roles') }}</a>
+        <a href="{{ baseUrl('/settings/roles') }}" @if($selected == 'roles') class="active" @endif>@icon('lock-open'){{ trans('settings.roles') }}</a>
     @endif
 </div>
\ No newline at end of file
index 4dfba1f0b5f10eedfbb2fc95f8cec6280c5b5bfa..98201da8fc7d82af4ad69c867f159531530c2647 100644 (file)
@@ -1,3 +1,7 @@
-<input type="checkbox" name="permissions[{{ $permission }}]"
-       @if(old('permissions'.$permission, false)|| (!old('display_name', false) && (isset($role) && $role->hasPermission($permission)))) checked="checked" @endif
-       value="true">
\ No newline at end of file
+
+@include('components.custom-checkbox', [
+       'name' => 'permissions[' . $permission . ']',
+       'value' => 'true',
+       'checked' => old('permissions'.$permission, false)|| (!old('display_name', false) && (isset($role) && $role->hasPermission($permission))),
+       'label' => $label
+])
\ No newline at end of file
index e61922da609096327383052c17ec5e8ac5db08b0..80a6fc3820d1bffb5c671652d02e0e56f409d391 100644 (file)
@@ -1,17 +1,16 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'roles'])
-@stop
-
 @section('body')
 
-    <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 class="container small">
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'roles'])
         </div>
-    </form>
+
+        <form action="{{ baseUrl("/settings/roles/new") }}" method="POST">
+            @include('settings.roles.form', ['title' => trans('settings.role_create')])
+        </form>
+    </div>
 
 @stop
index 6e447c3797b50965502a0f1e0aa27031e39318de..a2ea0d7281cd22fb30988048f2965cf0a067315d 100644 (file)
@@ -1,35 +1,44 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'roles'])
-@stop
-
 @section('body')
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ 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">
-
-                    @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="py-m">
+            @include('settings.navbar', ['selected' => 'roles'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading"> {{ trans('settings.role_delete') }}</h1>
+
+            <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">
+
+                @if($role->users->count() > 0)
                     <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>
+                        <p>{{ trans('settings.role_delete_users_assigned', ['userCount' => $role->users->count()]) }}</p>
+                        @include('form.role-select', ['options' => $roles, 'name' => 'migration_role_id'])
+                    </div>
+                @endif
+
+                <div class="grid half v-center">
+                    <div>
+                        <p class="text-neg">
+                            <strong>{{ trans('settings.role_delete_sure') }}</strong>
+                        </p>
                     </div>
-                </form>
-            </div>
+                    <div>
+                        <div class="form-group text-right">
+                            <a href="{{ baseUrl("/settings/roles/{$role->id}") }}" class="button outline">{{ trans('common.cancel') }}</a>
+                            <button type="submit" class="button primary">{{ trans('common.confirm') }}</button>
+                        </div>
+                    </div>
+                </div>
+
+
+            </form>
         </div>
 
     </div>
index 01ba53383595282cc8ceba368c912db01106e462..a7b81322977c59eb48780d4fd8a8b13a2e23e3ea 100644 (file)
@@ -1,17 +1,16 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'roles'])
-@stop
-
 @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 class="container small">
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'roles'])
         </div>
-    </form>
+
+        <form action="{{ baseUrl("/settings/roles/{$role->id}") }}" method="POST">
+            <input type="hidden" name="_method" value="PUT">
+            @include('settings.roles.form', ['model' => $role, 'title' => trans('settings.role_edit'), 'icon' => 'edit'])
+        </form>
+    </div>
+
 @stop
index 619229a655067328ae4f088f745550ed1dbbd761..6d723086714bbc844cdd49ab672571c037324c2c 100644 (file)
 {!! csrf_field() !!}
 
-<div class="col-md-9">
-    <div class="card">
-        <h3>@icon($icon) {{$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>
+<div class="card content-wrap">
+    <h1 class="list-heading">{{ $title }}</h1>
 
-                    @if(config('auth.method') === 'ldap')
-                        <div class="form-group">
-                            <label for="name">{{ trans('settings.role_external_auth_id') }}</label>
-                            @include('form/text', ['name' => 'external_auth_id'])
-                        </div>
-                    @endif
+    <div class="setting-list">
 
-                    <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 class="grid half">
+            <div>
+                <label class="setting-list-label">{{ trans('settings.role_details') }}</label>
+            </div>
+            <div>
+                <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>
 
-                <div class="col-md-6">
-
-                    <h5>{{ trans('settings.role_asset') }}</h5>
-                    <p>{{ trans('settings.role_asset_desc') }}</p>
-
-                    @if (isset($role) && $role->system_name === 'admin')
-                        <p>{{ trans('settings.role_asset_admins') }}</p>
-                    @endif
+                @if(config('auth.method') === 'ldap')
+                    <div class="form-group">
+                        <label for="name">{{ trans('settings.role_external_auth_id') }}</label>
+                        @include('form.text', ['name' => 'external_auth_id'])
+                    </div>
+                @endif
+            </div>
+        </div>
 
-                    <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.shelves_long') }}</td>
-                            <td>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-create-all']) {{ trans('settings.role_all') }}</label>
-                            </td>
-                            <td>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-view-own']) {{ trans('settings.role_own') }}</label>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-view-all']) {{ trans('settings.role_all') }}</label>
-                            </td>
-                            <td>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-update-own']) {{ trans('settings.role_own') }}</label>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-update-all']) {{ trans('settings.role_all') }}</label>
-                            </td>
-                            <td>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-delete-own']) {{ trans('settings.role_own') }}</label>
-                                <label>@include('settings/roles/checkbox', ['permission' => 'bookshelf-delete-all']) {{ trans('settings.role_all') }}</label>
-                            </td>
-                        </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 class="grid half" permissions-table>
+            <div>
+                <label class="setting-list-label">{{ trans('settings.role_system') }}</label>
+                <a href="#" permissions-table-toggle-all class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
             </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 class="toggle-switch-list">
+                <div>@include('settings.roles.checkbox', ['permission' => 'users-manage', 'label' => trans('settings.role_manage_users')])</div>
+                <div>@include('settings.roles.checkbox', ['permission' => 'user-roles-manage', 'label' => trans('settings.role_manage_roles')])</div>
+                <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-all', 'label' => trans('settings.role_manage_entity_permissions')])</div>
+                <div>@include('settings.roles.checkbox', ['permission' => 'restrictions-manage-own', 'label' => trans('settings.role_manage_own_entity_permissions')])</div>
+                <div>@include('settings.roles.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
             </div>
         </div>
+
+        <div>
+            <label class="setting-list-label">{{ trans('settings.role_asset') }}</label>
+            <p>{{ trans('settings.role_asset_desc') }}</p>
+
+            @if (isset($role) && $role->system_name === 'admin')
+                <p class="text-warn">{{ trans('settings.role_asset_admins') }}</p>
+            @endif
+
+            <table permissions-table class="table toggle-switch-list compact permissions-table">
+                <tr>
+                    <th width="20%">
+                        <a href="#" permissions-table-toggle-all class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </th>
+                    <th width="20%" permissions-table-toggle-all-in-column>{{ trans('common.create') }}</th>
+                    <th width="20%" permissions-table-toggle-all-in-column>{{ trans('common.view') }}</th>
+                    <th width="20%" permissions-table-toggle-all-in-column>{{ trans('common.edit') }}</th>
+                    <th width="20%" permissions-table-toggle-all-in-column>{{ trans('common.delete') }}</th>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.shelves_long') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-create-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-view-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-view-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'bookshelf-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.books') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'book-create-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'book-view-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'book-view-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'book-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'book-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'book-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'book-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.chapters') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-create-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-create-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-view-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-view-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'chapter-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.pages') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'page-create-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'page-create-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'page-view-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'page-view-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'page-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'page-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'page-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'page-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.images') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>@include('settings.roles.checkbox', ['permission' => 'image-create-all', 'label' => ''])</td>
+                    <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'image-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'image-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'image-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'image-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.attachments') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>@include('settings.roles.checkbox', ['permission' => 'attachment-create-all', 'label' => ''])</td>
+                    <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'attachment-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'attachment-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'attachment-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'attachment-delete-all', 'label' => trans('settings.role_all')])
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <div>{{ trans('entities.comments') }}</div>
+                        <a href="#" permissions-table-toggle-all-in-row class="text-small text-primary">{{ trans('common.toggle_all') }}</a>
+                    </td>
+                    <td>@include('settings.roles.checkbox', ['permission' => 'comment-create-all', 'label' => ''])</td>
+                    <td style="line-height:1.2;"><small class="faded">{{ trans('settings.role_controlled_by_asset') }}</small></td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'comment-update-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'comment-update-all', 'label' => trans('settings.role_all')])
+                    </td>
+                    <td>
+                        @include('settings.roles.checkbox', ['permission' => 'comment-delete-own', 'label' => trans('settings.role_own')])
+                        <br>
+                        @include('settings.roles.checkbox', ['permission' => 'comment-delete-all', 'label' => trans('settings.role_all')])
+                    </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 outline">{{ trans('settings.role_delete') }}</a>
+        @endif
+        <button type="submit" class="button primary">{{ trans('settings.role_save') }}</button>
+    </div>
+
 </div>
-<div class="col-md-3">
-    <div class="card">
-        <h3>@icon('users') {{ 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 href="{{ baseUrl("/settings/users/{$user->id}") }}">
-                                        @endif
-                                        {{ $user->name }}
-                                        @if(userCan('users-manage') || $currentUser->id == $user->id)
-                                    </a>
+
+<div class="card content-wrap auto-height">
+    <h2 class="list-heading">{{ trans('settings.role_users') }}</h2>
+    @if(isset($role) && count($role->users) > 0)
+        <div class="grid third">
+            @foreach($role->users as $user)
+                <div class="user-list-item">
+                    <div>
+                        <img class="avatar small" src="{{ $user->getAvatar(40) }}" alt="{{ $user->name }}">
+                    </div>
+                    <div>
+                        @if(userCan('users-manage') || $currentUser->id == $user->id)
+                            <a href="{{ baseUrl("/settings/users/{$user->id}") }}">
                                 @endif
-                            </td>
-                        </tr>
-                    @endforeach
-                </table>
-            @else
-                <p class="text-muted">
-                    {{ trans('settings.role_users_none') }}
-                </p>
-            @endif
+                                {{ $user->name }}
+                                @if(userCan('users-manage') || $currentUser->id == $user->id)
+                            </a>
+                        @endif
+                    </div>
+                </div>
+            @endforeach
         </div>
-    </div>
+    @else
+        <p class="text-muted">
+            {{ trans('settings.role_users_none') }}
+        </p>
+    @endif
 </div>
\ No newline at end of file
index 147d04bd17a642c6e7f470135fbd66f22c9fcf86..8eae235daf8a471906ed8d21edeb94f23e7c53a2 100644 (file)
@@ -1,35 +1,39 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'roles'])
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('lock-open') {{ 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="form-group">
-                    <a href="{{ baseUrl("/settings/roles/new") }}" class="button pos">{{ trans('settings.role_create') }}</a>
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'roles'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+
+            <div class="grid half v-center">
+                <h1 class="list-heading">{{ trans('settings.role_user_roles') }}</h1>
+
+                <div class="text-right">
+                    <a href="{{ baseUrl("/settings/roles/new") }}" class="button outline">{{ trans('settings.role_create') }}</a>
                 </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>
     </div>
 
index 32e40a4ae5271e51788e3c554eafa8947f43e6d8..72bf904fea1a7ec372e86aae2081b8c12a38c9b7 100644 (file)
@@ -1,30 +1,30 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-8 faded">
-        <div class="breadcrumbs">
-            <a href="{{ baseUrl('/shelves') }}" class="text-button">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
-            <span class="sep">&raquo;</span>
-            <a href="{{ baseUrl('/create-shelf') }}" class="text-button">@icon('add'){{ trans('entities.shelves_create') }}</a>
-        </div>
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('add') {{ trans('entities.shelves_create') }}</h3>
-            <div class="body">
-                <form action="{{ baseUrl("/shelves") }}" method="POST" enctype="multipart/form-data">
-                    @include('shelves/form', ['shelf' => null, 'books' => $books])
-                </form>
-            </div>
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                '/shelves' => [
+                    'text' => trans('entities.shelves'),
+                    'icon' => 'bookshelf',
+                ],
+                '/create-shelf' => [
+                    'text' => trans('entities.shelves_create'),
+                    'icon' => 'add',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.shelves_create') }}</h1>
+            <form action="{{ baseUrl("/shelves") }}" method="POST" enctype="multipart/form-data">
+                @include('shelves.form', ['shelf' => null, 'books' => $books])
+            </form>
         </div>
-    </div>
 
-    <p class="margin-top large"><br></p>
+    </div>
 
     @include('components.image-manager', ['imageType' => 'cover'])
 
index f3ad62456b18aa04003333ce0bed421260fee012..2a78227bda656ffd611c21dd5570b96c1e8ea3ac 100644 (file)
@@ -1,22 +1,29 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('shelves._breadcrumbs', ['shelf' => $shelf])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ trans('entities.shelves_delete') }}</h3>
-            <div class="body">
-                <p>{{ trans('entities.shelves_delete_explain', ['name' => $shelf->name]) }}</p>
-                <p class="text-neg">{{ trans('entities.shelves_delete_confirmation') }}</p>
-
-                <form action="{{ $shelf->getUrl() }}" method="POST">
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $shelf,
+                $shelf->getUrl('/delete') => [
+                    'text' => trans('entities.shelves_delete'),
+                    'icon' => 'delete',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('entities.shelves_delete') }}</h1>
+            <p>{{ trans('entities.shelves_delete_explain', ['name' => $shelf->name]) }}</p>
+
+            <div class="grid half">
+                <p class="text-neg">
+                    <strong>{{ trans('entities.shelves_delete_confirmation') }}</strong>
+                </p>
+
+                <form action="{{ $shelf->getUrl() }}" method="POST" class="text-right">
                     {!! csrf_field() !!}
                     <input type="hidden" name="_method" value="DELETE">
 
@@ -24,6 +31,8 @@
                     <button type="submit" class="button">{{ trans('common.confirm') }}</button>
                 </form>
             </div>
+
+
         </div>
     </div>
 
index ab88051e5b18b616b495fa4e9b5cd894b3429274..8b99e8b515ba56a64700a490fbe44dad7598dbfa 100644 (file)
@@ -1,24 +1,27 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('shelves._breadcrumbs', ['shelf' => $shelf])
-    </div>
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('edit') {{ trans('entities.shelves_edit') }}</h3>
-            <div class="body">
-                <form action="{{ $shelf->getUrl() }}" method="POST">
-                    <input type="hidden" name="_method" value="PUT">
-                    @include('shelves/form', ['model' => $shelf])
-                </form>
-            </div>
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $shelf,
+                $shelf->getUrl('/edit') => [
+                    'text' => trans('entities.shelves_edit'),
+                    'icon' => 'edit',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.shelves_edit') }}</h1>
+            <form action="{{ $shelf->getUrl() }}" method="POST">
+                <input type="hidden" name="_method" value="PUT">
+                @include('shelves.form', ['model' => $shelf])
+            </form>
         </div>
     </div>
-@include('components.image-manager', ['imageType' => 'cover'])
+
+    @include('components.image-manager', ['imageType' => 'cover'])
 @stop
\ No newline at end of file
diff --git a/resources/views/shelves/export.blade.php b/resources/views/shelves/export.blade.php
deleted file mode 100644 (file)
index 462ad79..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-<!doctype html>
-<html lang="en">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-    <title>{{ $book->name }}</title>
-
-    <style>
-        @if (!app()->environment('testing'))
-        {!! file_get_contents(public_path('/dist/export-styles.css')) !!}
-        @endif
-        .page-break {
-            page-break-after: always;
-        }
-        .chapter-hint {
-            color: #888;
-            margin-top: 32px;
-        }
-        .chapter-hint + h1 {
-            margin-top: 0;
-        }
-        ul.contents ul li {
-            list-style: circle;
-        }
-        @media screen {
-            .page-break {
-                border-top: 1px solid #DDD;
-            }
-        }
-    </style>
-    @yield('head')
-</head>
-<body>
-<div class="container">
-    <div class="row">
-        <div class="col-md-8 col-md-offset-2">
-            <div class="page-content">
-
-                <h1 style="font-size: 4.8em">{{$book->name}}</h1>
-
-                <p>{{ $book->description }}</p>
-
-                @if(count($bookChildren) > 0)
-                <ul class="contents">
-                    @foreach($bookChildren as $bookChild)
-                        <li><a href="#{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</a></li>
-                        @if($bookChild->isA('chapter') && count($bookChild->pages) > 0)
-                            <ul>
-                                @foreach($bookChild->pages as $page)
-                                    <li><a href="#page-{{$page->id}}">{{ $page->name }}</a></li>
-                                @endforeach
-                            </ul>
-                        @endif
-                    @endforeach
-                </ul>
-                @endif
-
-                @foreach($bookChildren as $bookChild)
-                    <div class="page-break"></div>
-                    <h1 id="{{$bookChild->getType()}}-{{$bookChild->id}}">{{ $bookChild->name }}</h1>
-                    @if($bookChild->isA('chapter'))
-                        <p>{{ $bookChild->description }}</p>
-                        @if(count($bookChild->pages) > 0)
-                            @foreach($bookChild->pages as $page)
-                                <div class="page-break"></div>
-                                <div class="chapter-hint">{{$bookChild->name}}</div>
-                                <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
-                                {!! $page->html !!}
-                            @endforeach
-                        @endif
-                    @else
-                        {!! $bookChild->html !!}
-                    @endif
-                @endforeach
-
-            </div>
-        </div>
-    </div>
-</div>
-</body>
-</html>
index fb6fee1151682ab77aeb4bcc2fb58dfb80b5eb99..8a270a894a2eda4a1a56555e7b67229f4bea4a35 100644 (file)
@@ -1,48 +1,44 @@
-
 {{ csrf_field() }}
+
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form/text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name'])
 </div>
 
 <div class="form-group description-input">
     <label for="description">{{ trans('common.description') }}</label>
-    @include('form/textarea', ['name' => 'description'])
+    @include('form.textarea', ['name' => 'description'])
 </div>
 
-<div shelf-sort class="row">
-    <div class="col-md-6">
-        <div  class="form-group">
-            <label for="books">{{ trans('entities.shelves_books') }}</label>
-            <input type="hidden" id="books-input" name="books"
-                   value="{{ isset($shelf) ? $shelf->books->implode('id', ',') : '' }}">
-            <div class="scroll-box">
-                <div class="scroll-box-item text-small text-muted instruction">
-                    {{ trans('entities.shelves_drag_books') }}
-                </div>
-                <div class="scroll-box-item scroll-box-placeholder" style="display: none;">
-                    <a href="#" class="text-muted">@icon('book') ...</a>
-                </div>
-                @if (isset($shelfBooks) && count($shelfBooks) > 0)
-                    @foreach ($shelfBooks as $book)
-                        <div data-id="{{ $book->id }}" class="scroll-box-item">
-                            <a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
-                        </div>
-                    @endforeach
-                @endif
+<div shelf-sort class="grid half gap-xl">
+    <div class="form-group">
+        <label for="books">{{ trans('entities.shelves_books') }}</label>
+        <input type="hidden" id="books-input" name="books"
+               value="{{ isset($shelf) ? $shelf->books->implode('id', ',') : '' }}">
+        <div class="scroll-box">
+            <div class="scroll-box-item text-small text-muted instruction">
+                {{ trans('entities.shelves_drag_books') }}
             </div>
-        </div>
-    </div>
-    <div class="col-md-6">
-        <div class="form-group">
-            <label for="books">{{ trans('entities.shelves_add_books') }}</label>
-            <div class="scroll-box">
-                @foreach ($books as $book)
+            <div class="scroll-box-item scroll-box-placeholder" style="display: none;">
+                <a href="#" class="text-muted">@icon('book') ...</a>
+            </div>
+            @if (isset($shelfBooks) && count($shelfBooks) > 0)
+                @foreach ($shelfBooks as $book)
                     <div data-id="{{ $book->id }}" class="scroll-box-item">
                         <a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
                     </div>
                 @endforeach
-            </div>
+            @endif
+        </div>
+    </div>
+    <div class="form-group">
+        <label for="books">{{ trans('entities.shelves_add_books') }}</label>
+        <div class="scroll-box">
+            @foreach ($books as $book)
+                <div data-id="{{ $book->id }}" class="scroll-box-item">
+                    <a href="{{ $book->getUrl() }}" class="text-book">@icon('book'){{ $book->name }}</a>
+                </div>
+            @endforeach
         </div>
     </div>
 </div>
@@ -80,5 +76,5 @@
 
 <div class="form-group text-right">
     <a href="{{ isset($shelf) ? $shelf->getUrl() : baseUrl('/shelves') }}" class="button outline">{{ trans('common.cancel') }}</a>
-    <button type="submit" class="button pos">{{ trans('entities.shelves_save') }}</button>
+    <button type="submit" class="button primary">{{ trans('entities.shelves_save') }}</button>
 </div>
\ No newline at end of file
index b70b5166ed57c84ab890e68d54244f5e4c0571e6..25b35b9efab4ff624c19a27b77010790d38309f6 100644 (file)
@@ -1,18 +1,21 @@
-<div class="bookshelf-grid-item grid-card"  data-entity-type="bookshelf" data-entity-id="{{$bookshelf->id}}">
-    <div class="featured-image-container">
-        <a href="{{$bookshelf->getUrl()}}" title="{{$bookshelf->name}}">
-            <img src="{{$bookshelf->getBookCover()}}" alt="{{$bookshelf->name}}">
-        </a>
+<a href="{{$shelf->getUrl()}}" class="bookshelf-grid-item grid-card"
+   data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
+    <div class="bg-shelf featured-image-container-wrap">
+        <div class="featured-image-container" @if($shelf->cover) style="background-image: url('{{ $shelf->getBookCover() }}')"@endif>
+        </div>
+        @icon('bookshelf')
     </div>
     <div class="grid-card-content">
-        <h2><a class="break-text" href="{{$bookshelf->getUrl()}}" title="{{$bookshelf->name}}">{{$bookshelf->getShortName(35)}}</a></h2>
-        @if(isset($bookshelf->searchSnippet))
-            <p >{!! $bookshelf->searchSnippet !!}</p>
+        <h2>{{$shelf->getShortName(35)}}</h2>
+        @if(isset($shelf->searchSnippet))
+            <p class="text-muted">{!! $shelf->searchSnippet !!}</p>
         @else
-            <p >{{ $bookshelf->getExcerpt(130) }}</p>
+            <p class="text-muted">{{ $shelf->getExcerpt(130) }}</p>
         @endif
     </div>
     <div class="grid-card-footer text-muted text-small">
-        <span>@include('partials.entity-meta', ['entity' => $bookshelf])</span>
+        @icon('star')<span title="{{$shelf->created_at->toDayDateTimeString()}}">{{ trans('entities.meta_created', ['timeLength' => $shelf->created_at->diffForHumans()]) }}</span>
+        <br>
+        @icon('edit')<span title="{{ $shelf->updated_at->toDayDateTimeString() }}">{{ trans('entities.meta_updated', ['timeLength' => $shelf->updated_at->diffForHumans()]) }}</span>
     </div>
-</div>
\ No newline at end of file
+</a>
\ No newline at end of file
index a887a843e578e518ef08bb0d23a03d53aa9ec683..8cf959b1e49529092107324fff48e9e74bc8185e 100644 (file)
@@ -1,48 +1,49 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-xs-6 faded">
-        <div class="action-buttons text-left">
-            @include('shelves/view-toggle', ['shelvesViewType' => $shelvesViewType])
-        </div>
-    </div>
-    <div class="col-xs-6 faded">
-        <div class="action-buttons">
+@section('body')
+    @include('shelves.list', ['shelves' => $shelves, 'view' => $view])
+@stop
+
+@section('right')
+
+    <div class="actions mb-xl">
+        <h5>{{ trans('common.actions') }}</h5>
+        <div class="icon-list text-primary">
             @if($currentUser->can('bookshelf-create-all'))
-                <a href="{{ baseUrl("/create-shelf") }}" class="text-pos text-button">@icon('add'){{ trans('entities.shelves_create') }}</a>
+                <a href="{{ baseUrl("/create-shelf") }}" class="icon-list-item">
+                    <span>@icon('add')</span>
+                    <span>{{ trans('entities.shelves_new_action') }}</span>
+                </a>
             @endif
+            @include('partials.view-toggle', ['view' => $view, 'type' => 'shelf'])
         </div>
     </div>
+
 @stop
 
-@section('sidebar')
+@section('left')
     @if($recents)
-        <div id="recents" class="card">
-            <h3>@icon('view') {{ trans('entities.recently_viewed') }}</h3>
-            @include('partials/entity-list', ['entities' => $recents, 'style' => 'compact'])
+        <div id="recents" class="mb-xl">
+            <h5>{{ trans('entities.recently_viewed') }}</h5>
+            @include('partials.entity-list', ['entities' => $recents, 'style' => 'compact'])
         </div>
     @endif
 
-    <div id="popular" class="card">
-        <h3>@icon('popular') {{ trans('entities.shelves_popular') }}</h3>
+    <div id="popular" class="mb-xl">
+        <h5>{{ trans('entities.shelves_popular') }}</h5>
         @if(count($popular) > 0)
-            @include('partials/entity-list', ['entities' => $popular, 'style' => 'compact'])
+            @include('partials.entity-list', ['entities' => $popular, 'style' => 'compact'])
         @else
-            <div class="body text-muted">{{ trans('entities.shelves_popular_empty') }}</div>
+            <div class="text-muted">{{ trans('entities.shelves_popular_empty') }}</div>
         @endif
     </div>
 
-    <div id="new" class="card">
-        <h3>@icon('star-circle') {{ trans('entities.shelves_new') }}</h3>
+    <div id="new" class="mb-xl">
+        <h5>{{ trans('entities.shelves_new') }}</h5>
         @if(count($new) > 0)
-            @include('partials/entity-list', ['entities' => $new, 'style' => 'compact'])
+            @include('partials.entity-list', ['entities' => $new, 'style' => 'compact'])
         @else
-            <div class="body text-muted">{{ trans('entities.shelves_new_empty') }}</div>
+            <div class="text-muted">{{ trans('entities.shelves_new_empty') }}</div>
         @endif
     </div>
-@stop
-
-@section('body')
-    @include('shelves/list', ['shelves' => $shelves, 'shelvesViewType' => $shelvesViewType])
-    <p><br></p>
 @stop
\ No newline at end of file
index 0b8e79fe549122331868e15baf9355b89851960b..c9c9670c5833c84e02b09b919decc73a152cf0e2 100644 (file)
@@ -1,10 +1,21 @@
-<div class="shelf entity-list-item"  data-entity-type="bookshelf" data-entity-id="{{$bookshelf->id}}">
-    <h4 class="text-shelf"><a class="text-bookshelf entity-list-item-link" href="{{$bookshelf->getUrl()}}">@icon('bookshelf')<span class="entity-list-item-name break-text">{{$bookshelf->name}}</span></a></h4>
-    <div class="entity-item-snippet">
-        @if(isset($bookshelf->searchSnippet))
-            <p class="text-muted break-text">{!! $bookshelf->searchSnippet !!}</p>
-        @else
-            <p class="text-muted break-text">{{ $bookshelf->getExcerpt() }}</p>
-        @endif
+<a href="{{ $shelf->getUrl() }}" class="shelf entity-list-item" data-entity-type="bookshelf" data-entity-id="{{$shelf->id}}">
+    <div class="entity-list-item-image bg-shelf @if($shelf->image_id) has-image @endif" style="background-image: url('{{ $shelf->getBookCover() }}')">
+        @icon('bookshelf')
     </div>
+    <div class="content py-xs">
+        <h4 class="entity-list-item-name break-text">{{ $shelf->name }}</h4>
+        <div class="entity-item-snippet">
+            <p class="text-muted break-text mb-none">{{ $shelf->getExcerpt() }}</p>
+        </div>
+    </div>
+</a>
+<div class="entity-shelf-books grid third gap-y-xs entity-list-item-children">
+    @foreach($shelf->books as $book)
+        <div>
+            <a href="{{ $book->getUrl('?shelf=' . $shelf->id) }}" class="entity-chip text-book">
+                @icon('book')
+                {{ $book->name }}
+            </a>
+        </div>
+    @endforeach
 </div>
\ No newline at end of file
index ff11d2d67934b78965e68fbfe7fd2635188eee49..70787f7e805ce9990d5403dadff7b58538d774a5 100644 (file)
@@ -1,18 +1,29 @@
 
-<div class="container{{ $shelvesViewType === 'list' ? ' small' : '' }}">
-    <h1>{{ trans('entities.shelves') }}</h1>
+<div class="content-wrap mt-m card">
+
+    <div class="grid half v-center">
+        <h1 class="list-heading">{{ trans('entities.shelves') }}</h1>
+        <div class="text-right">
+            @include('partials.sort', ['options' => $sortOptions, 'order' => $order, 'sort' => $sort, 'type' => 'bookshelves'])
+        </div>
+    </div>
+
     @if(count($shelves) > 0)
-        @if($shelvesViewType === 'grid')
+        @if($view === 'list')
+            <div class="entity-list">
+                @foreach($shelves as $index => $shelf)
+                    @if ($index !== 0)
+                        <hr class="my-m">
+                    @endif
+                    @include('shelves.list-item', ['shelf' => $shelf])
+                @endforeach
+            </div>
+        @else
             <div class="grid third">
                 @foreach($shelves as $key => $shelf)
-                    @include('shelves/grid-item', ['bookshelf' => $shelf])
+                    @include('shelves.grid-item', ['shelf' => $shelf])
                 @endforeach
             </div>
-        @else
-            @foreach($shelves as $shelf)
-                @include('shelves/list-item', ['bookshelf' => $shelf])
-                <hr>
-            @endforeach
         @endif
         <div>
             {!! $shelves->render() !!}
@@ -23,4 +34,5 @@
             <a href="{{ baseUrl("/create-shelf") }}" class="button outline">@icon('edit'){{ trans('entities.create_now') }}</a>
         @endif
     @endif
-</div>
\ No newline at end of file
+
+</div>
diff --git a/resources/views/shelves/permissions.blade.php b/resources/views/shelves/permissions.blade.php
new file mode 100644 (file)
index 0000000..df50be8
--- /dev/null
@@ -0,0 +1,32 @@
+@extends('simple-layout')
+
+@section('body')
+
+    <div class="container small">
+
+        <div class="my-s">
+            @include('partials.breadcrumbs', ['crumbs' => [
+                $shelf,
+                $shelf->getUrl('/permissions') => [
+                    'text' => trans('entities.shelves_permissions'),
+                    'icon' => 'lock',
+                ]
+            ]])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('entities.shelves_permissions') }}</h1>
+            @include('form.entity-permissions', ['model' => $shelf])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h2 class="list-heading">{{ trans('entities.shelves_copy_permissions_to_books') }}</h2>
+            <p>{{ trans('entities.shelves_copy_permissions_explain') }}</p>
+            <form action="{{ $shelf->getUrl('/copy-permissions') }}" method="post" class="text-right">
+                {{ csrf_field() }}
+                <button class="button">{{ trans('entities.shelves_copy_permissions') }}</button>
+            </form>
+        </div>
+    </div>
+
+@stop
diff --git a/resources/views/shelves/restrictions.blade.php b/resources/views/shelves/restrictions.blade.php
deleted file mode 100644 (file)
index 472078a..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-@extends('simple-layout')
-
-@section('toolbar')
-    <div class="col-sm-12 faded">
-        @include('shelves._breadcrumbs', ['shelf' => $shelf])
-    </div>
-@stop
-
-@section('body')
-
-    <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('lock') {{ trans('entities.shelves_permissions') }}</h3>
-            <div class="body">
-                @include('form/restriction-form', ['model' => $shelf])
-            </div>
-        </div>
-
-        <p>&nbsp;</p>
-
-        <div class="card">
-            <h3>@icon('copy') {{ trans('entities.shelves_copy_permissions_to_books') }}</h3>
-            <div class="body">
-                <p>{{ trans('entities.shelves_copy_permissions_explain') }}</p>
-                <form action="{{ $shelf->getUrl('/copy-permissions') }}" method="post" class="text-right">
-                    {{ csrf_field() }}
-                    <button class="button">{{ trans('entities.shelves_copy_permissions') }}</button>
-                </form>
-            </div>
-        </div>
-    </div>
-
-@stop
index 2aae2c6ff3dbd27cdca72ae20162374a5c54b184..3a9d599519a95035e7d4a8cb8dfbcca23303669f 100644 (file)
@@ -1,42 +1,59 @@
-@extends('sidebar-layout')
+@extends('tri-layout')
 
-@section('toolbar')
-    <div class="col-sm-6 col-xs-1  faded">
-        @include('shelves._breadcrumbs', ['shelf' => $shelf])
+@section('body')
+
+    <div class="mb-s">
+        @include('partials.breadcrumbs', ['crumbs' => [
+            $shelf,
+        ]])
     </div>
-    <div class="col-sm-6 col-xs-11">
-        <div class="action-buttons faded">
-            @if(userCan('bookshelf-update', $shelf))
-                <a href="{{ $shelf->getUrl('/edit') }}" class="text-button text-primary">@icon('edit'){{ trans('common.edit') }}</a>
-            @endif
-            @if(userCan('restrictions-manage', $shelf) || userCan('bookshelf-delete', $shelf))
-                <div dropdown class="dropdown-container">
-                    <a dropdown-toggle class="text-primary text-button">@icon('more'){{ trans('common.more') }}</a>
-                    <ul>
-                        @if(userCan('restrictions-manage', $shelf))
-                            <li><a href="{{ $shelf->getUrl('/permissions') }}" class="text-primary">@icon('lock'){{ trans('entities.permissions') }}</a></li>
+
+    <div class="card content-wrap">
+        <h1 class="break-text">{{$shelf->name}}</h1>
+        <div class="book-content">
+            <p class="text-muted">{!! nl2br(e($shelf->description)) !!}</p>
+            @if(count($books) > 0)
+                <div class="entity-list">
+                    @foreach($books as $book)
+                        @include('books.list-item', ['book' => $book])
+                    @endforeach
+                </div>
+            @else
+                <div class="mt-xl">
+                    <hr>
+                    <p class="text-muted italic mt-xl mb-m">{{ trans('entities.shelves_empty_contents') }}</p>
+                    <div class="icon-list inline block">
+                        @if(userCan('book-create-all') && userCan('bookshelf-update', $shelf))
+                            <a href="{{ $shelf->getUrl('/create-book') }}" class="icon-list-item text-book">
+                                <span class="icon">@icon('add')</span>
+                                <span>{{ trans('entities.books_create') }}</span>
+                            </a>
                         @endif
-                        @if(userCan('bookshelf-delete', $shelf))
-                            <li><a href="{{ $shelf->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('common.delete') }}</a></li>
+                        @if(userCan('bookshelf-update', $shelf))
+                            <a href="{{ $shelf->getUrl('/edit') }}" class="icon-list-item text-bookshelf">
+                                <span class="icon">@icon('edit')</span>
+                                <span>{{ trans('entities.shelves_edit_and_assign') }}</span>
+                            </a>
                         @endif
-                    </ul>
+                    </div>
                 </div>
             @endif
         </div>
     </div>
+
 @stop
 
-@section('sidebar')
+@section('left')
 
     @if($shelf->tags->count() > 0)
-        <section>
+        <div id="tags" class="mb-xl">
             @include('components.tag-list', ['entity' => $shelf])
-        </section>
+        </div>
     @endif
 
-    <div class="card entity-details">
-        <h3>@icon('info') {{ trans('common.details') }}</h3>
-        <div class="body text-small text-muted blended-links">
+    <div id="details" class="mb-xl">
+        <h5>{{ trans('common.details') }}</h5>
+        <div class="text-small text-muted blended-links">
             @include('partials.entity-meta', ['entity' => $shelf])
             @if($shelf->restricted)
                 <div class="active-restriction">
     </div>
 
     @if(count($activity) > 0)
-        <div class="activity card">
-            <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
-            @include('partials/activity-list', ['activity' => $activity])
+        <div class="mb-xl">
+            <h5>{{ trans('entities.recent_activity') }}</h5>
+            @include('partials.activity-list', ['activity' => $activity])
         </div>
     @endif
 @stop
 
-@section('body')
+@section('right')
+    <div class="actions mb-xl">
+        <h5>{{ trans('common.actions') }}</h5>
+        <div class="icon-list text-primary">
 
-    <div class="container small nopad">
-        <h1 class="break-text">{{$shelf->name}}</h1>
-        <div class="book-content">
-            <p class="text-muted">{!! nl2br(e($shelf->description)) !!}</p>
-            @if(count($books) > 0)
-            <div class="page-list">
-                <hr>
-                @foreach($books as $book)
-                    @include('books/list-item', ['book' => $book])
-                    <hr>
-                @endforeach
-            </div>
-            @else
-            <p>
-                <hr>
-                <span class="text-muted italic">{{ trans('entities.shelves_empty_contents') }}</span>
-                @if(userCan('bookshelf-create', $shelf))
-                    <br>
-                    <a href="{{ $shelf->getUrl('/edit') }}" class="button outline bookshelf">{{ trans('entities.shelves_edit_and_assign') }}</a>
-                @endif
-            </p>
+            @if(userCan('book-create-all') && userCan('bookshelf-update', $shelf))
+                <a href="{{ $shelf->getUrl('/create-book') }}" class="icon-list-item">
+                    <span class="icon">@icon('add')</span>
+                    <span>{{ trans('entities.books_new_action') }}</span>
+                </a>
             @endif
 
-    </div>
+            <hr class="primary-background">
+
+            @if(userCan('bookshelf-update', $shelf))
+                <a href="{{ $shelf->getUrl('/edit') }}" class="icon-list-item">
+                    <span>@icon('edit')</span>
+                    <span>{{ trans('common.edit') }}</span>
+                </a>
+            @endif
+
+            @if(userCan('restrictions-manage', $shelf))
+                <a href="{{ $shelf->getUrl('/permissions') }}" class="icon-list-item">
+                    <span>@icon('lock')</span>
+                    <span>{{ trans('entities.permissions') }}</span>
+                </a>
+            @endif
 
+            @if(userCan('bookshelf-delete', $shelf))
+                <a href="{{ $shelf->getUrl('/delete') }}" class="icon-list-item">
+                    <span>@icon('delete')</span>
+                    <span>{{ trans('common.delete') }}</span>
+                </a>
+            @endif
+
+        </div>
+    </div>
 @stop
+
+
+
+
diff --git a/resources/views/shelves/view-toggle.blade.php b/resources/views/shelves/view-toggle.blade.php
deleted file mode 100644 (file)
index 785e8ca..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-<form action="{{ baseUrl("/settings/users/{$currentUser->id}/switch-shelf-view") }}" method="POST" class="inline">
-    {!! csrf_field() !!}
-    {!! method_field('PATCH') !!}
-    <input type="hidden" value="{{ $shelvesViewType === 'list'? 'grid' : 'list' }}" name="view_type">
-    @if ($shelvesViewType === 'list')
-        <button type="submit" class="text-pos text-button">@icon('grid'){{ trans('common.grid_view') }}</button>
-    @else
-        <button type="submit" class="text-pos text-button">@icon('list'){{ trans('common.list_view') }}</button>
-    @endif
-</form>
\ No newline at end of file
diff --git a/resources/views/sidebar-layout.blade.php b/resources/views/sidebar-layout.blade.php
deleted file mode 100644 (file)
index bf853c0..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-@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">@icon('caret-right-circle')
-            </div>
-            <div class="scroll-body">
-                @yield('sidebar')
-            </div>
-        </div>
-
-        <div class="content flex @yield('body-wrap-classes')">
-            @yield('body')
-        </div>
-    </div>
-
-
-@stop
index eeb4129e0497f32132b9d8c6915bec2d9def7307..a57aaebef64f39f45460c27dfc6d955ff2ce74d4 100644 (file)
@@ -1,20 +1,7 @@
 @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">
@@ -23,5 +10,4 @@
         </div>
     </div>
 
-
 @stop
diff --git a/resources/views/tri-layout.blade.php b/resources/views/tri-layout.blade.php
new file mode 100644 (file)
index 0000000..00e9df2
--- /dev/null
@@ -0,0 +1,39 @@
+@extends('base')
+
+@section('body-class', 'tri-layout')
+
+@section('content')
+
+    <div class="tri-layout-mobile-tabs text-primary" >
+        <div class="grid half no-break no-gap">
+            <div class="tri-layout-mobile-tab px-m py-s" tri-layout-mobile-tab="info">
+                {{ trans('common.tab_info') }}
+            </div>
+            <div class="tri-layout-mobile-tab px-m py-s active" tri-layout-mobile-tab="content">
+                {{ trans('common.tab_content') }}
+            </div>
+        </div>
+    </div>
+
+    <div class="tri-layout-container" tri-layout @yield('container-attrs') >
+
+        <div class="tri-layout-left print-hidden pt-m" id="sidebar">
+            <div class="tri-layout-left-contents">
+                @yield('left')
+            </div>
+        </div>
+
+        <div class="@yield('body-wrap-classes') tri-layout-middle">
+            <div class="tri-layout-middle-contents">
+                @yield('body')
+            </div>
+        </div>
+
+        <div class="tri-layout-right print-hidden pt-m">
+            <div class="tri-layout-right-contents">
+                @yield('right')
+            </div>
+        </div>
+    </div>
+
+@stop
index d42ca9f51e91da14ab37d07bf9e72eccb6fcfbe7..cd5d75f8fb9b9da6a7d3e0c0569cab7485c7fb7d 100644 (file)
@@ -1,25 +1,30 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'users'])
-@stop
-
 @section('body')
 
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('users-add') {{ 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>
-                </form>
-            </div>
+
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'users'])
+        </div>
+
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ trans('settings.users_add_new') }}</h1>
+
+            <form action="{{ baseUrl("/settings/users/create") }}" method="post">
+                {!! csrf_field() !!}
+
+                <div class="setting-list">
+                    @include('users.form')
+                </div>
+
+                <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 primary" type="submit">{{ trans('common.save') }}</button>
+                </div>
+
+            </form>
+
         </div>
     </div>
 
index 39cd12200bf454ef8c283089cda42de0e864d275..15ad7a9ec66df6678ed3777f261bb55f7e6dedf2 100644 (file)
@@ -1,27 +1,30 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'users'])
-@stop
-
 @section('body')
-
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('delete') {{ 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 outline">{{ trans('common.cancel') }}</a>
-                    <button type="submit" class="button neg">{{ trans('common.confirm') }}</button>
-                </form>
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'users'])
+        </div>
+
+        <div class="card content-wrap auto-height">
+            <h1 class="list-heading">{{ trans('settings.users_delete') }}</h1>
+
+            <p>{{ trans('settings.users_delete_warning', ['userName' => $user->name]) }}</p>
+
+            <div class="grid half">
+                <p class="text-neg"><strong>{{ trans('settings.users_delete_confirm') }}</strong></p>
+                <div>
+                    <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="POST" class="text-right">
+                        {!! 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 primary">{{ trans('common.confirm') }}</button>
+                    </form>
+                </div>
             </div>
+
         </div>
     </div>
-
 @stop
index 1b0514f9cdc4de07a3cfcee151e3abb62c2ff44e..b9ad052c7b1d93925a3f8dd529660f5e6e868678 100644 (file)
@@ -1,91 +1,91 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'users'])
-@stop
-
 @section('body')
-
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('edit') {{ $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">
-                            {!! 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 class="py-m">
+            @include('settings.navbar', ['selected' => 'users'])
+        </div>
 
-                                @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">
+        <div class="card content-wrap">
+            <h1 class="list-heading">{{ $user->id === $currentUser->id ? trans('settings.users_edit_profile') : trans('settings.users_edit') }}</h1>
+            <form action="{{ baseUrl("/settings/users/{$user->id}") }}" method="post">
+                {!! csrf_field() !!}
+                <input type="hidden" name="_method" value="PUT">
 
-                                    @foreach(trans('settings.language_select') as $lang => $label)
-                                        <option @if(setting()->getUser($user, 'language', config('app.default_locale')) === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
-                                    @endforeach
-                                </select>
-                            </div>
+                <div class="setting-list">
+                    @include('users.form', ['model' => $user, 'authMethod' => $authMethod])
+
+                    <div class="grid half gap-xl">
+                        <div>
+                            <label for="user-avatar" class="setting-list-label">{{ trans('settings.users_avatar') }}</label>
+                            <p class="small">{{ trans('settings.users_avatar_desc') }}</p>
+                        </div>
+                        <div>
+                            @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>
-                    <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 class="grid half gap-xl v-center">
+                        <div>
+                            <label for="user-language" class="setting-list-label">{{ trans('settings.users_preferred_language') }}</label>
+                            <p class="small">
+                                {{ trans('settings.users_preferred_language_desc') }}
+                            </p>
+                        </div>
+                        <div>
+                            <select name="setting[language]" id="user-language">
+                                @foreach(trans('settings.language_select') as $lang => $label)
+                                    <option @if(setting()->getUser($user, 'language', config('app.default_locale')) === $lang) selected @endif value="{{ $lang }}">{{ $label }}</option>
+                                @endforeach
+                            </select>
+                        </div>
                     </div>
-                </form>
-            </div>
+
+                </div>
+
+                <div class="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="button outline">{{ trans('settings.users_delete') }}</a>
+                    @endif
+                    <button class="button primary" type="submit">{{ trans('common.save') }}</button>
+                </div>
+            </form>
         </div>
 
         @if($currentUser->id === $user->id && count($activeSocialDrivers) > 0)
-            <div class="card">
-                <h3>@icon('login')  {{ 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('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</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 class="card content-wrap auto-height">
+                <h2 class="list-heading">{{ trans('settings.users_social_accounts') }}</h2>
+                <p class="text-muted">{{ trans('settings.users_social_accounts_info') }}</p>
+                <div class="container">
+                    <div class="grid third">
+                        @foreach($activeSocialDrivers as $driver => $enabled)
+                            <div class="text-center mb-m">
+                                <div>@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</div>
+                                <div>
+                                    @if($user->hasSocialAccount($driver))
+                                        <a href="{{ baseUrl("/login/service/{$driver}/detach") }}" class="button small outline">{{ trans('settings.users_social_disconnect') }}</a>
+                                    @else
+                                        <a href="{{ baseUrl("/login/service/{$driver}") }}" class="button small outline">{{ trans('settings.users_social_connect') }}</a>
+                                    @endif
                                 </div>
-                            @endforeach
-                        </div>
+                            </div>
+                        @endforeach
                     </div>
                 </div>
             </div>
         @endif
-
-
     </div>
 
-    <p class="margin-top large"><br></p>
     @include('components.image-manager', ['imageType' => 'user'])
-@stop
\ No newline at end of file
+@stop
diff --git a/resources/views/users/form.blade.php b/resources/views/users/form.blade.php
new file mode 100644 (file)
index 0000000..96beb7b
--- /dev/null
@@ -0,0 +1,70 @@
+
+@if($authMethod === 'system' && $user->system_name == 'public')
+    <p class="mb-none text-warn">{{ trans('settings.users_system_public') }}</p>
+@endif
+
+<div class="pt-m">
+    <label class="setting-list-label">{{ trans('settings.users_details') }}</label>
+    @if($authMethod === 'standard')
+        <p class="small">{{ trans('settings.users_details_desc') }}</p>
+    @endif
+    @if($authMethod === 'ldap' || $authMethod === 'system')
+        <p class="small">{{ trans('settings.users_details_desc_no_email') }}</p>
+    @endif
+    <div class="grid half mt-m gap-xl">
+        <div>
+            <label for="name">{{ trans('auth.name') }}</label>
+            @include('form.text', ['name' => 'name'])
+        </div>
+        <div>
+            @if($authMethod !== 'ldap' || userCan('users-manage'))
+                <label for="email">{{ trans('auth.email') }}</label>
+                @include('form.text', ['name' => 'email'])
+            @endif
+        </div>
+    </div>
+</div>
+
+@if($authMethod === 'ldap' && userCan('users-manage'))
+    <div class="grid half gap-xl v-center">
+        <div>
+            <label class="setting-list-label">{{ trans('settings.users_external_auth_id') }}</label>
+            <p class="small">{{ trans('settings.users_external_auth_id_desc') }}</p>
+        </div>
+        <div>
+            @include('form.text', ['name' => 'external_auth_id'])
+        </div>
+    </div>
+@endif
+
+@if(userCan('users-manage'))
+    <div>
+        <label for="role" class="setting-list-label">{{ trans('settings.users_role') }}</label>
+        <p class="small">{{ trans('settings.users_role_desc') }}</p>
+        <div class="mt-m">
+            @include('form.role-checkboxes', ['name' => 'roles', 'roles' => $roles])
+        </div>
+    </div>
+@endif
+
+@if($authMethod === 'standard')
+    <div>
+        <label class="setting-list-label">{{ trans('settings.users_password') }}</label>
+        <p class="small">{{ trans('settings.users_password_desc') }}</p>
+        @if(isset($model))
+            <p class="small">
+                {{ trans('settings.users_password_warning') }}
+            </p>
+        @endif
+        <div class="grid half mt-m gap-xl">
+            <div>
+                <label for="password">{{ trans('auth.password') }}</label>
+                @include('form.password', ['name' => 'password'])
+            </div>
+            <div>
+                <label for="password-confirm">{{ trans('auth.password_confirm') }}</label>
+                @include('form.password', ['name' => 'password-confirm'])
+            </div>
+        </div>
+    </div>
+@endif
\ No newline at end of file
diff --git a/resources/views/users/forms/ldap.blade.php b/resources/views/users/forms/ldap.blade.php
deleted file mode 100644 (file)
index f6e8b4c..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-<div class="form-group">
-    <label for="name">{{ trans('auth.name') }}</label>
-    @include('form.text', ['name' => 'name'])
-</div>
-
-@if(userCan('users-manage'))
-<div class="form-group">
-    <label for="email">{{ trans('auth.email') }}</label>
-    @include('form.text', ['name' => 'email'])
-</div>
-@endif
-
-@if(userCan('users-manage'))
-    <div class="form-group">
-        <label for="role">{{ trans('settings.users_role') }}</label>
-        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
-    </div>
-@endif
-
-@if(userCan('users-manage'))
-    <div class="form-group">
-        <label for="external_auth_id">{{ trans('settings.users_external_auth_id') }}</label>
-        @include('form.text', ['name' => 'external_auth_id'])
-    </div>
-@endif
\ No newline at end of file
diff --git a/resources/views/users/forms/standard.blade.php b/resources/views/users/forms/standard.blade.php
deleted file mode 100644 (file)
index fa71236..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-<div class="form-group">
-    <label for="name">{{ 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>
-
-@if(userCan('users-manage'))
-    <div class="form-group">
-        <label for="role">{{ trans('settings.users_role') }}</label>
-        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
-    </div>
-@endif
-
-@if(isset($model))
-    <div class="form-group">
-        <span class="text-muted">
-            {{ trans('settings.users_password_warning') }}
-        </span>
-    </div>
-@endif
-
-<div class="form-group">
-    <label for="password">{{ trans('auth.password') }}</label>
-    @include('form.password', ['name' => 'password'])
-</div>
-
-<div class="form-group">
-    <label for="password-confirm">{{ trans('auth.password_confirm') }}</label>
-    @include('form.password', ['name' => 'password-confirm'])
-</div>
\ No newline at end of file
diff --git a/resources/views/users/forms/system.blade.php b/resources/views/users/forms/system.blade.php
deleted file mode 100644 (file)
index 6243010..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-@if($user->system_name == 'public')
-    <p>{{ trans('settings.users_system_public') }}</p>
-@endif
-
-<div class="form-group">
-    <label for="name">{{ 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>
-
-@if(userCan('users-manage'))
-    <div class="form-group">
-        <label for="role">{{ trans('settings.users_role') }}</label>
-        @include('form/role-checkboxes', ['name' => 'roles', 'roles' => $roles])
-    </div>
-@endif
-
index fd05a7a15cac196970e4c3f8e8cf10b109269a93..af6b4d4f93e70d25c81a7dfff0af76e2479503c1 100644 (file)
@@ -1,74 +1,66 @@
 @extends('simple-layout')
 
-@section('toolbar')
-    @include('settings/navbar', ['selected' => 'users'])
-@stop
-
 @section('body')
     <div class="container small">
-        <p>&nbsp;</p>
-        <div class="card">
-            <h3>@icon('users') {{ 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 class="py-m">
+            @include('settings.navbar', ['selected' => 'users'])
+        </div>
+
+        <div class="card content-wrap">
+
+            <div class="grid right-focus v-center">
+                <h1 class="list-heading">{{ trans('settings.users') }}</h1>
+
+                <div class="text-right">
+                    <div class="block inline mr-s">
+                        <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>
+                    @if(userCan('users-manage'))
+                        <a href="{{ baseUrl("/settings/users/create") }}" style="margin-top: 0;" class="outline button">{{ trans('settings.users_add_new') }}</a>
+                    @endif
                 </div>
+            </div>
 
-                <table class="table">
+            {{--TODO - Add last login--}}
+            <table class="table">
+                <tr>
+                    <th></th>
+                    <th>
+                        <a href="{{ sortUrl('/settings/users', $listDetails, ['sort' => 'name']) }}">{{ trans('auth.name') }}</a>
+                        /
+                        <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>
-                        <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>
+                        <td class="text-center" 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 }} <br> <span class="text-muted">{{ $user->email }}</span>
+                                    @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>
-                    @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 }}
-                                        @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}") }}">
-                                        @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>
+                @endforeach
+            </table>
 
+            <div>
+                {{ $users->links() }}
             </div>
         </div>
 
index 4f67f1be2a59ba90b880988425a942cb4d21b7ee..e2689790f185516d5d34408ba21dca05c138f3f0 100644 (file)
-@extends('sidebar-layout')
-
-@section('toolbar')
-    <div class="col-sm-6 col-xs-1 faded">
-        <div class="breadcrumbs">
-            <a href="{{ $user->getProfileUrl() }}" class="text-button">@icon('user'){{ $user->name }}</a>
-        </div>
-    </div>
-@stop
-
-@section('sidebar')
-    <div class="card" id="recent-activity">
-        <h3>@icon('time') {{ trans('entities.recent_activity') }}</h3>
-        @include('partials/activity-list', ['activity' => $activity])
-    </div>
-@stop
+@extends('simple-layout')
 
 @section('body')
 
-    <div class="container small">
+    <div class="container pt-xl">
 
-        <div class="padded-top large"></div>
+        <div class="grid right-focus reverse-collapse">
 
-        <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 id="recent-user-activity" class="mb-xl">
+                    <h5>{{ trans('entities.recent_activity') }}</h5>
+                    @include('partials.activity-list', ['activity' => $activity])
                 </div>
             </div>
-            <div class="col-md-5 text-bigger" id="content-counts">
-                <div class="text-muted">{{ trans('entities.profile_created_content') }}</div>
-                <a href="#recent-books">
-                    <div class="text-book">
-                        @icon('book')  {{ trans_choice('entities.x_books', $assetCounts['books']) }}
-                    </div>
-                </a>
-                <a href="#recent-chapters">
-                    <div class="text-chapter">
-                        @icon('chapter') {{ trans_choice('entities.x_chapters', $assetCounts['chapters']) }}
-                    </div>
-                </a>
-                <a href="#recent-pages">
-                    <div class="text-page">
-                        @icon('page') {{ trans_choice('entities.x_pages', $assetCounts['pages']) }}
+
+            <div>
+                <div class="card content-wrap auto-height">
+                    <div class="grid half v-center">
+                        <div>
+                            <div class="mr-m float left">
+                                <img class="avatar square huge" src="{{ $user->getAvatar(120) }}" alt="{{ $user->name }}">
+                            </div>
+                            <div>
+                                <h4 class="mt-md">{{ $user->name }}</h4>
+                                <p class="text-muted">
+                                    {{ trans('entities.profile_user_for_x', ['time' => $user->created_at->diffForHumans(null, true)]) }}
+                                </p>
+                            </div>
+                        </div>
+                        <div id="content-counts">
+                            <div class="text-muted">{{ trans('entities.profile_created_content') }}</div>
+                            <div class="grid half v-center no-row-gap">
+                                <div class="icon-list">
+                                    <a href="#recent-pages" class="text-page icon-list-item">
+                                        <span>@icon('page')</span>
+                                        <span>{{ trans_choice('entities.x_pages', $assetCounts['pages']) }}</span>
+                                    </a>
+                                    <a href="#recent-chapters" class="text-chapter icon-list-item">
+                                        <span>@icon('chapter')</span>
+                                        <span>{{ trans_choice('entities.x_chapters', $assetCounts['chapters']) }}</span>
+                                    </a>
+                                </div>
+                                <div class="icon-list">
+                                    <a href="#recent-books" class="text-book icon-list-item">
+                                        <span>@icon('book')</span>
+                                        <span>{{ trans_choice('entities.x_books', $assetCounts['books']) }}</span>
+                                    </a>
+                                    <a href="#recent-shelves" class="text-bookshelf icon-list-item">
+                                        <span>@icon('bookshelf')</span>
+                                        <span>{{ trans_choice('entities.x_shelves', $assetCounts['shelves']) }}</span>
+                                    </a>
+                                </div>
+                            </div>
+
+                        </div>
                     </div>
-                </a>
-            </div>
-        </div>
+                </div>
+
+                <div class="card content-wrap auto-height book-contents">
+                    <h2 id="recent-pages" class="list-heading">
+                        {{ trans('entities.recently_created_pages') }}
+                        @if (count($recentlyCreated['pages']) > 0)
+                            <a href="{{ baseUrl('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:page}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
+                        @endif
+                    </h2>
+                    @if (count($recentlyCreated['pages']) > 0)
+                        @include('partials.entity-list', ['entities' => $recentlyCreated['pages'], 'showPath' => true])
+                    @else
+                        <p class="text-muted">{{ trans('entities.profile_not_created_pages', ['userName' => $user->name]) }}</p>
+                    @endif
+                </div>
 
+                <div class="card content-wrap auto-height book-contents">
+                    <h2 id="recent-chapters" class="list-heading">
+                        {{ trans('entities.recently_created_chapters') }}
+                        @if (count($recentlyCreated['chapters']) > 0)
+                            <a href="{{ baseUrl('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:chapter}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
+                        @endif
+                    </h2>
+                    @if (count($recentlyCreated['chapters']) > 0)
+                        @include('partials.entity-list', ['entities' => $recentlyCreated['chapters'], 'showPath' => true])
+                    @else
+                        <p class="text-muted">{{ trans('entities.profile_not_created_chapters', ['userName' => $user->name]) }}</p>
+                    @endif
+                </div>
 
-        <hr class="even">
-        <h3 id="recent-pages">{{ 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
+                <div class="card content-wrap auto-height book-contents">
+                    <h2 id="recent-books" class="list-heading">
+                        {{ trans('entities.recently_created_books') }}
+                        @if (count($recentlyCreated['books']) > 0)
+                            <a href="{{ baseUrl('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:book}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
+                        @endif
+                    </h2>
+                    @if (count($recentlyCreated['books']) > 0)
+                        @include('partials.entity-list', ['entities' => $recentlyCreated['books'], 'showPath' => true])
+                    @else
+                        <p class="text-muted">{{ trans('entities.profile_not_created_books', ['userName' => $user->name]) }}</p>
+                    @endif
+                </div>
+
+                <div class="card content-wrap auto-height book-contents">
+                    <h2 id="recent-shelves" class="list-heading">
+                        {{ trans('entities.recently_created_shelves') }}
+                        @if (count($recentlyCreated['shelves']) > 0)
+                            <a href="{{ baseUrl('/search?term=' . urlencode('{created_by:'.$user->id.'} {type:bookshelf}') ) }}" class="text-small ml-s">{{ trans('common.view_all') }}</a>
+                        @endif
+                    </h2>
+                    @if (count($recentlyCreated['shelves']) > 0)
+                        @include('partials.entity-list', ['entities' => $recentlyCreated['shelves'], 'showPath' => true])
+                    @else
+                        <p class="text-muted">{{ trans('entities.profile_not_created_shelves', ['userName' => $user->name]) }}</p>
+                    @endif
+                </div>
+            </div>
+
+        </div>
 
-        <hr class="even">
-        <h3 id="recent-chapters">{{ 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
 
-        <hr class="even">
-        <h3 id="recent-books">{{ 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 d3c5f46d3ccdce717e167d4e545b4f19639feb68..695f61654a13c97b68f0dba8bf84a2226971ca80 100644 (file)
@@ -10,7 +10,6 @@ Route::group(['middleware' => 'auth'], function () {
         ->where('path', '.*$');
 
     Route::group(['prefix' => 'pages'], function() {
-        Route::get('/recently-created', 'PageController@showRecentlyCreated');
         Route::get('/recently-updated', 'PageController@showRecentlyUpdated');
     });
 
@@ -24,9 +23,12 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{slug}', 'BookshelfController@show');
         Route::put('/{slug}', 'BookshelfController@update');
         Route::delete('/{slug}', 'BookshelfController@destroy');
-        Route::get('/{slug}/permissions', 'BookshelfController@showRestrict');
-        Route::put('/{slug}/permissions', 'BookshelfController@restrict');
+        Route::get('/{slug}/permissions', 'BookshelfController@showPermissions');
+        Route::put('/{slug}/permissions', 'BookshelfController@permissions');
         Route::post('/{slug}/copy-permissions', 'BookshelfController@copyPermissions');
+
+        Route::get('/{shelfSlug}/create-book', 'BookController@create');
+        Route::post('/{shelfSlug}/create-book', 'BookController@store');
     });
 
     Route::get('/create-book', 'BookController@create');
@@ -40,8 +42,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::delete('/{id}', 'BookController@destroy');
         Route::get('/{slug}/sort-item', 'BookController@getSortItem');
         Route::get('/{slug}', 'BookController@show');
-        Route::get('/{bookSlug}/permissions', 'BookController@showRestrict');
-        Route::put('/{bookSlug}/permissions', 'BookController@restrict');
+        Route::get('/{bookSlug}/permissions', 'BookController@showPermissions');
+        Route::put('/{bookSlug}/permissions', 'BookController@permissions');
         Route::get('/{slug}/delete', 'BookController@showDelete');
         Route::get('/{bookSlug}/sort', 'BookController@sort');
         Route::put('/{bookSlug}/sort', 'BookController@saveSort');
@@ -65,8 +67,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::post('/{bookSlug}/page/{pageSlug}/copy', 'PageController@copy');
         Route::get('/{bookSlug}/page/{pageSlug}/delete', 'PageController@showDelete');
         Route::get('/{bookSlug}/draft/{pageId}/delete', 'PageController@showDeleteDraft');
-        Route::get('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@showRestrict');
-        Route::put('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@restrict');
+        Route::get('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@showPermissions');
+        Route::put('/{bookSlug}/page/{pageSlug}/permissions', 'PageController@permissions');
         Route::put('/{bookSlug}/page/{pageSlug}', 'PageController@update');
         Route::delete('/{bookSlug}/page/{pageSlug}', 'PageController@destroy');
         Route::delete('/{bookSlug}/draft/{pageId}', 'PageController@destroyDraft');
@@ -75,7 +77,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/page/{pageSlug}/revisions', 'PageController@showRevisions');
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}', 'PageController@showRevision');
         Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}/changes', 'PageController@showRevisionChanges');
-        Route::get('/{bookSlug}/page/{pageSlug}/revisions/{revId}/restore', 'PageController@restoreRevision');
+        Route::put('/{bookSlug}/page/{pageSlug}/revisions/{revId}/restore', 'PageController@restoreRevision');
         Route::delete('/{bookSlug}/page/{pageSlug}/revisions/{revId}/delete', 'PageController@destroyRevision');
 
         // Chapters
@@ -88,11 +90,11 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@showMove');
         Route::put('/{bookSlug}/chapter/{chapterSlug}/move', 'ChapterController@move');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/edit', 'ChapterController@edit');
-        Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showRestrict');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showPermissions');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterController@exportPdf');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterController@exportHtml');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterController@exportPlainText');
-        Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@restrict');
+        Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@permissions');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
         Route::delete('/{bookSlug}/chapter/{chapterSlug}', 'ChapterController@destroy');
     });
@@ -155,6 +157,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/search', 'SearchController@search');
     Route::get('/search/book/{bookId}', 'SearchController@searchBook');
     Route::get('/search/chapter/{bookId}', 'SearchController@searchChapter');
+    Route::get('/search/entity/siblings', 'SearchController@searchSiblings');
 
     // Other Pages
     Route::get('/', 'HomeController@index');
@@ -176,6 +179,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/users/{id}/delete', 'UserController@delete');
         Route::patch('/users/{id}/switch-book-view', 'UserController@switchBookView');
         Route::patch('/users/{id}/switch-shelf-view', 'UserController@switchShelfView');
+        Route::patch('/users/{id}/change-sort/{type}', 'UserController@changeSort');
+        Route::patch('/users/{id}/update-expansion-preference/{key}', 'UserController@updateExpansionPreference');
         Route::post('/users/create', 'UserController@store');
         Route::get('/users/{id}', 'UserController@edit');
         Route::put('/users/{id}', 'UserController@update');
index 5ccb1415e6fed57e2bced5e33e6d04b966cadf2a..5923ef377700734f931b6a56098bfe14a18e255d 100644 (file)
@@ -409,4 +409,41 @@ class LdapTest extends BrowserKitTest
             ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => $this->mockUser->name]);
     }
 
+    protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
+    {
+        app('config')->set([
+            'services.ldap.server' => $serverString
+        ]);
+
+        // Standard mocks
+        $this->mockLdap->shouldReceive('setVersion')->once();
+        $this->mockLdap->shouldReceive('setOption')->times(2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)->andReturn(['count' => 1, 0 => [
+            'uid' => [$this->mockUser->name],
+            'cn' => [$this->mockUser->name],
+            'dn' => ['dc=test' . config('services.ldap.base_dn')]
+        ]]);
+        $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
+        $this->mockEscapes(2);
+
+        $this->mockLdap->shouldReceive('connect')->once()
+            ->with($expectedHost, $expectedPort)->andReturn($this->resourceId);
+        $this->mockUserLogin();
+    }
+
+    public function test_ldap_port_provided_on_host_if_host_is_full_uri()
+    {
+        $hostName = 'ldaps://bookstack:8080';
+        $this->checkLdapReceivesCorrectDetails($hostName, $hostName, 389);
+    }
+
+    public function test_ldap_port_parsed_from_server_if_host_is_not_full_uri()
+    {
+        $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com:8080', 'ldap.bookstack.com', 8080);
+    }
+
+    public function test_default_ldap_port_used_if_not_in_server_string_and_not_uri()
+    {
+        $this->checkLdapReceivesCorrectDetails('ldap.bookstack.com', 'ldap.bookstack.com', 389);
+    }
 }
index bdba812d59a16593516e96f99e0a6a78e64393a8..158fb5ca184e2075e0cef15339bf71075576dc12 100644 (file)
@@ -48,7 +48,7 @@ class BookShelfTest extends TestCase
     public function test_shelves_page_contains_create_link()
     {
         $resp = $this->asEditor()->get('/shelves');
-        $resp->assertElementContains('a', 'Create New Shelf');
+        $resp->assertElementContains('a', 'New Shelf');
     }
 
     public function test_shelves_create()
@@ -99,9 +99,11 @@ class BookShelfTest extends TestCase
     {
         $shelf = Bookshelf::first();
         $resp = $this->asAdmin()->get($shelf->getUrl());
+        $resp->assertSee($shelf->getUrl('/create-book'));
         $resp->assertSee($shelf->getUrl('/edit'));
         $resp->assertSee($shelf->getUrl('/permissions'));
         $resp->assertSee($shelf->getUrl('/delete'));
+        $resp->assertElementContains('a', 'New Book');
         $resp->assertElementContains('a', 'Edit');
         $resp->assertElementContains('a', 'Permissions');
         $resp->assertElementContains('a', 'Delete');
@@ -148,6 +150,32 @@ class BookShelfTest extends TestCase
         $this->assertDatabaseHas('bookshelves_books', ['bookshelf_id' => $shelf->id, 'book_id' => $booksToInclude[1]->id]);
     }
 
+    public function test_shelf_create_new_book()
+    {
+        $shelf = Bookshelf::first();
+        $resp = $this->asEditor()->get($shelf->getUrl('/create-book'));
+
+        $resp->assertSee('Create New Book');
+        $resp->assertSee($shelf->getShortName());
+
+        $testName = 'Test Book in Shelf Name';
+
+        $createBookResp = $this->asEditor()->post($shelf->getUrl('/create-book'), [
+            'name' => $testName,
+            'description' => 'Book in shelf description'
+        ]);
+        $createBookResp->assertRedirect();
+
+        $newBook = Book::query()->orderBy('id', 'desc')->first();
+        $this->assertDatabaseHas('bookshelves_books', [
+            'bookshelf_id' => $shelf->id,
+            'book_id' => $newBook->id,
+        ]);
+
+        $resp = $this->asEditor()->get($shelf->getUrl());
+        $resp->assertSee($testName);
+    }
+
     public function test_shelf_delete()
     {
         $shelf = Bookshelf::first();
@@ -185,4 +213,30 @@ class BookShelfTest extends TestCase
         $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
     }
 
+    public function test_bookshelves_show_in_breadcrumbs_if_in_context()
+    {
+        $shelf = Bookshelf::first();
+        $shelfBook = $shelf->books()->first();
+        $shelfPage = $shelfBook->pages()->first();
+        $this->asAdmin();
+
+        $bookVisit = $this->get($shelfBook->getUrl());
+        $bookVisit->assertElementNotContains('.breadcrumbs', 'Shelves');
+        $bookVisit->assertElementNotContains('.breadcrumbs', $shelf->getShortName());
+
+        $this->get($shelf->getUrl());
+        $bookVisit = $this->get($shelfBook->getUrl());
+        $bookVisit->assertElementContains('.breadcrumbs', 'Shelves');
+        $bookVisit->assertElementContains('.breadcrumbs', $shelf->getShortName());
+
+        $pageVisit = $this->get($shelfPage->getUrl());
+        $pageVisit->assertElementContains('.breadcrumbs', 'Shelves');
+        $pageVisit->assertElementContains('.breadcrumbs', $shelf->getShortName());
+
+        $this->get('/books');
+        $pageVisit = $this->get($shelfPage->getUrl());
+        $pageVisit->assertElementNotContains('.breadcrumbs', 'Shelves');
+        $pageVisit->assertElementNotContains('.breadcrumbs', $shelf->getShortName());
+    }
+
 }
index 4114d4af9c816dc04f4576185566491bf9c01537..3eb50a41279fa6cf56783f3d108a961efed93438 100644 (file)
@@ -197,11 +197,11 @@ class EntitySearchTest extends TestCase
 
         $pageSearch = $this->get('/ajax/search/entities?term=' . urlencode($page->name));
         $pageSearch->assertSee($page->name);
-        $pageSearch->assertSee($chapter->getShortName());
-        $pageSearch->assertSee($page->book->getShortName());
+        $pageSearch->assertSee($chapter->getShortName(42));
+        $pageSearch->assertSee($page->book->getShortName(42));
 
         $chapterSearch = $this->get('/ajax/search/entities?term=' . urlencode($chapter->name));
         $chapterSearch->assertSee($chapter->name);
-        $chapterSearch->assertSee($chapter->book->getShortName());
+        $chapterSearch->assertSee($chapter->book->getShortName(42));
     }
 }
index ef9597d328496aac1ba8ee292917003053e5b426..a3fb1cfe11833591a5d1282aeda43fdce23f9eb9 100644 (file)
@@ -65,9 +65,7 @@ class EntityTest extends BrowserKitTest
             ->click('Sort')
             ->seePageIs($bookToSort->getUrl() . '/sort')
             ->seeStatusCode(200)
-            ->see($bookToSort->name)
-            // Ensure page shows other books
-            ->see($books[1]->name);
+            ->see($bookToSort->name);
     }
 
     public function test_book_sort_item_returns_book_content()
@@ -224,15 +222,6 @@ class EntityTest extends BrowserKitTest
             ->click('Revisions')->seeStatusCode(200);
     }
 
-    public function test_recently_created_pages_view()
-    {
-        $user = $this->getEditor();
-        $content = $this->createEntityChainBelongingToUser($user);
-
-        $this->asAdmin()->visit('/pages/recently-created')
-            ->seeInNthElement('.entity-list .page', 0, $content['page']->name);
-    }
-
     public function test_recently_updated_pages_view()
     {
         $user = $this->getEditor();
index 86abadf147a788d63684a8a71a3a616ef3d57766..03cf03956db0bf534fb097c4687fab5e4207eca1 100644 (file)
@@ -71,51 +71,6 @@ class PageContentTest extends TestCase
         $pageResp->assertSee($content);
     }
 
-    public function test_page_revision_views_viewable()
-    {
-        $this->asEditor();
-
-        $pageRepo = app(PageRepo::class);
-        $page = Page::first();
-        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
-        $pageRevision = $page->revisions->last();
-
-        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
-        $revisionView->assertStatus(200);
-        $revisionView->assertSee('new content');
-
-        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id . '/changes');
-        $revisionView->assertStatus(200);
-        $revisionView->assertSee('new content');
-    }
-
-    public function test_page_revision_restore_updates_content()
-    {
-        $this->asEditor();
-
-        $pageRepo = app(PageRepo::class);
-        $page = Page::first();
-        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'initial page revision testing']);
-        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
-        $page =  Page::find($page->id);
-
-
-        $pageView = $this->get($page->getUrl());
-        $pageView->assertDontSee('abc123');
-        $pageView->assertDontSee('def456');
-
-        $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
-        $restoreReq = $this->get($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
-        $page =  Page::find($page->id);
-
-        $restoreReq->assertStatus(302);
-        $restoreReq->assertRedirect($page->getUrl());
-
-        $pageView = $this->get($page->getUrl());
-        $pageView->assertSee('abc123');
-        $pageView->assertSee('def456');
-    }
-
     public function test_page_content_scripts_escaped_by_default()
     {
         $this->asEditor();
@@ -143,4 +98,37 @@ class PageContentTest extends TestCase
         $pageView->assertDontSee(htmlentities($script));
     }
 
+    public function test_duplicate_ids_does_not_break_page_render()
+    {
+        $this->asEditor();
+        $pageA = Page::first();
+        $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
+
+        $content = '<ul id="bkmrk-xxx-%28"></ul> <ul id="bkmrk-xxx-%28"></ul>';
+        $pageA->html = $content;
+        $pageA->save();
+
+        $pageB->html = '<ul id="bkmrk-xxx-%28"></ul> <p>{{@'. $pageA->id .'#test}}</p>';
+        $pageB->save();
+
+        $pageView = $this->get($pageB->getUrl());
+        $pageView->assertSuccessful();
+    }
+
+    public function test_duplicate_ids_fixed_on_page_save()
+    {
+        $this->asEditor();
+        $page = Page::first();
+
+        $content = '<p id="bkmrk-test">test a</p>'."\n".'<p id="bkmrk-test">test b</p>';
+        $pageSave = $this->put($page->getUrl(), [
+            'name' => $page->name,
+            'html' => $content,
+            'summary' => ''
+        ]);
+        $pageSave->assertRedirect();
+
+        $updatedPage = Page::where('id', '=', $page->id)->first();
+        $this->assertEquals(substr_count($updatedPage->html, "bkmrk-test\""), 1);
+    }
 }
index 1ebd5860b054f2ec153e166cfd5ab4d65cf0e6bd..f15651f39b672824520bbdb096962cc2fe81b256 100644 (file)
@@ -87,13 +87,13 @@ class PageDraftTest extends BrowserKitTest
             ->visit($book->getUrl() . '/create-page')
             ->visit($chapter->getUrl() . '/create-page')
             ->visit($book->getUrl())
-            ->seeInElement('.page-list', 'New Page');
+            ->seeInElement('.book-contents', 'New Page');
         
         $this->asAdmin()
             ->visit($book->getUrl())
-            ->dontSeeInElement('.page-list', 'New Page')
+            ->dontSeeInElement('.book-contents', 'New Page')
             ->visit($chapter->getUrl())
-            ->dontSeeInElement('.page-list', 'New Page');
+            ->dontSeeInElement('.book-contents', 'New Page');
     }
 
 }
index 015320dd7b1208e7c2ae4aa74d49f13f5498b457..521ea79a4760ee6bcfa30c3497b8882f1a267ddb 100644 (file)
@@ -1,11 +1,55 @@
 <?php namespace Entity;
 
-
 use BookStack\Entities\Page;
+use BookStack\Entities\Repos\PageRepo;
 use Tests\TestCase;
 
 class PageRevisionTest extends TestCase
 {
+    public function test_page_revision_views_viewable()
+    {
+        $this->asEditor();
+
+        $pageRepo = app(PageRepo::class);
+        $page = Page::first();
+        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
+        $pageRevision = $page->revisions->last();
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new content');
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id . '/changes');
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new content');
+    }
+
+    public function test_page_revision_restore_updates_content()
+    {
+        $this->asEditor();
+
+        $pageRepo = app(PageRepo::class);
+        $page = Page::first();
+        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page abc123', 'html' => '<p>new contente def456</p>', 'summary' => 'initial page revision testing']);
+        $pageRepo->updatePage($page, $page->book_id, ['name' => 'updated page again', 'html' => '<p>new content</p>', 'summary' => 'page revision testing']);
+        $page =  Page::find($page->id);
+
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertDontSee('abc123');
+        $pageView->assertDontSee('def456');
+
+        $revToRestore = $page->revisions()->where('name', 'like', '%abc123')->first();
+        $restoreReq = $this->put($page->getUrl() . '/revisions/' . $revToRestore->id . '/restore');
+        $page =  Page::find($page->id);
+
+        $restoreReq->assertStatus(302);
+        $restoreReq->assertRedirect($page->getUrl());
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertSee('abc123');
+        $pageView->assertSee('def456');
+    }
 
     public function test_page_revision_count_increments_on_update()
     {
index 86cae7893a4b2e4e70f9ae60e1cedb6b4d001e2a..2c8b8d5c032929e100b512c86ec91ccc544b0c50 100644 (file)
@@ -62,7 +62,7 @@ class HomepageTest extends TestCase
         $this->asEditor();
         $homeVisit = $this->get('/');
         $homeVisit->assertSee('Books');
-        $homeVisit->assertSee('book-grid-item grid-card');
+        $homeVisit->assertSee('grid-card');
         $homeVisit->assertSee('grid-card-content');
         $homeVisit->assertSee('grid-card-footer');
         $homeVisit->assertSee('featured-image-container');
index 351132ffef908eb4e0fcf02fc9f98b903116fb97..a7f681a37d1597d72a254d4e7b549e8c2e310202 100644 (file)
@@ -131,12 +131,12 @@ class RestrictionsTest extends BrowserKitTest
         $bookUrl = $book->getUrl();
         $this->actingAs($this->viewer)
             ->visit($bookUrl)
-            ->dontSeeInElement('.action-buttons', 'New Page')
-            ->dontSeeInElement('.action-buttons', 'New Chapter');
+            ->dontSeeInElement('.actions', 'New Page')
+            ->dontSeeInElement('.actions', 'New Chapter');
         $this->actingAs($this->user)
             ->visit($bookUrl)
-            ->seeInElement('.action-buttons', 'New Page')
-            ->seeInElement('.action-buttons', 'New Chapter');
+            ->seeInElement('.actions', 'New Page')
+            ->seeInElement('.actions', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
 
@@ -144,8 +144,8 @@ class RestrictionsTest extends BrowserKitTest
             ->see('You do not have permission')->seePageIs('/');
         $this->forceVisit($bookUrl . '/create-page')
             ->see('You do not have permission')->seePageIs('/');
-        $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
-            ->dontSeeInElement('.action-buttons', 'New Chapter');
+        $this->visit($bookUrl)->dontSeeInElement('.actions', 'New Page')
+            ->dontSeeInElement('.actions', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'create']);
 
@@ -159,8 +159,8 @@ class RestrictionsTest extends BrowserKitTest
             ->type('test content', 'html')
             ->press('Save Page')
             ->seePageIs($bookUrl . '/page/test-page');
-        $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
-            ->seeInElement('.action-buttons', 'New Chapter');
+        $this->visit($bookUrl)->seeInElement('.actions', 'New Page')
+            ->seeInElement('.actions', 'New Chapter');
     }
 
     public function test_book_update_restriction()
@@ -255,13 +255,13 @@ class RestrictionsTest extends BrowserKitTest
         $chapterUrl = $chapter->getUrl();
         $this->actingAs($this->user)
             ->visit($chapterUrl)
-            ->seeInElement('.action-buttons', 'New Page');
+            ->seeInElement('.actions', 'New Page');
 
         $this->setEntityRestrictions($chapter, ['view', 'delete', 'update']);
 
         $this->forceVisit($chapterUrl . '/create-page')
             ->see('You do not have permission')->seePageIs('/');
-        $this->visit($chapterUrl)->dontSeeInElement('.action-buttons', 'New Page');
+        $this->visit($chapterUrl)->dontSeeInElement('.actions', 'New Page');
 
         $this->setEntityRestrictions($chapter, ['view', 'create']);
 
@@ -272,7 +272,7 @@ class RestrictionsTest extends BrowserKitTest
             ->press('Save Page')
             ->seePageIs($chapter->book->getUrl() . '/page/test-page');
 
-        $this->visit($chapterUrl)->seeInElement('.action-buttons', 'New Page');
+        $this->visit($chapterUrl)->seeInElement('.actions', 'New Page');
     }
 
     public function test_chapter_update_restriction()
@@ -535,8 +535,8 @@ class RestrictionsTest extends BrowserKitTest
         $bookUrl = $book->getUrl();
         $this->actingAs($this->viewer)
             ->visit($bookUrl)
-            ->dontSeeInElement('.action-buttons', 'New Page')
-            ->dontSeeInElement('.action-buttons', 'New Chapter');
+            ->dontSeeInElement('.actions', 'New Page')
+            ->dontSeeInElement('.actions', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'delete', 'update']);
 
@@ -544,8 +544,8 @@ class RestrictionsTest extends BrowserKitTest
             ->see('You do not have permission')->seePageIs('/');
         $this->forceVisit($bookUrl . '/create-page')
             ->see('You do not have permission')->seePageIs('/');
-        $this->visit($bookUrl)->dontSeeInElement('.action-buttons', 'New Page')
-            ->dontSeeInElement('.action-buttons', 'New Chapter');
+        $this->visit($bookUrl)->dontSeeInElement('.actions', 'New Page')
+            ->dontSeeInElement('.actions', 'New Chapter');
 
         $this->setEntityRestrictions($book, ['view', 'create']);
 
@@ -559,8 +559,8 @@ class RestrictionsTest extends BrowserKitTest
             ->type('test content', 'html')
             ->press('Save Page')
             ->seePageIs($bookUrl . '/page/test-page');
-        $this->visit($bookUrl)->seeInElement('.action-buttons', 'New Page')
-            ->seeInElement('.action-buttons', 'New Chapter');
+        $this->visit($bookUrl)->seeInElement('.actions', 'New Page')
+            ->seeInElement('.actions', 'New Chapter');
     }
 
     public function test_book_update_restriction_override()
@@ -645,11 +645,9 @@ class RestrictionsTest extends BrowserKitTest
     {
         $firstBook = Book::first();
         $secondBook = Book::find(2);
-        $thirdBook = Book::find(3);
 
         $this->setEntityRestrictions($firstBook, ['view', 'update']);
         $this->setEntityRestrictions($secondBook, ['view']);
-        $this->setEntityRestrictions($thirdBook, ['view', 'update']);
 
         // Test sort page visibility
         $this->actingAs($this->user)->visit($secondBook->getUrl() . '/sort')
@@ -657,9 +655,7 @@ class RestrictionsTest extends BrowserKitTest
                 ->seePageIs('/');
 
         // Check sort page on first book
-        $this->actingAs($this->user)->visit($firstBook->getUrl() . '/sort')
-                ->see($thirdBook->name)
-                ->dontSee($secondBook->name);
+        $this->actingAs($this->user)->visit($firstBook->getUrl() . '/sort');
     }
 
     public function test_book_sort_permission() {
index da2abb0bdf23c335fe5aa29a1053ce71deac8007..5bbdcf0bbb60c5f0c8ecf15d04f2285bbab5f7f5 100644 (file)
@@ -215,7 +215,7 @@ class RolesTest extends BrowserKitTest
         $this->checkAccessPermission('bookshelf-create-all', [
             '/create-shelf'
         ], [
-            '/shelves' => 'Create New Shelf'
+            '/shelves' => 'New Shelf'
         ]);
 
         $this->visit('/create-shelf')
@@ -632,8 +632,8 @@ class RolesTest extends BrowserKitTest
     {
         $user = \BookStack\Auth\User::first();
         $this->asAdmin()->visit('/settings/users/' . $user->id)
-            ->seeElement('#roles-admin')
-            ->seeElement('#roles-public');
+            ->seeElement('[name="roles[admin]"]')
+            ->seeElement('[name="roles[public]"]');
     }
 
     public function test_public_role_visible_in_role_listing()
index 373d9eb5a9c9ce99655c407dee51664e5b9f43eb..35ffda821ef35262f1b135fcde4c8cbc90c7a519 100644 (file)
@@ -28,16 +28,6 @@ class AttachmentTest extends TestCase
         return $this->call('POST', '/attachments/upload', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
     }
 
-    /**
-     * Get the expected upload path for a file.
-     * @param $fileName
-     * @return string
-     */
-    protected function getUploadPath($fileName)
-    {
-        return 'uploads/files/' . Date('Y-m-M') . '/' . $fileName;
-    }
-
     /**
      * Delete all uploaded files.
      * To assist with cleanup.
@@ -64,17 +54,34 @@ class AttachmentTest extends TestCase
             'order' => 1,
             'created_by' => $admin->id,
             'updated_by' => $admin->id,
-            'path' => $this->getUploadPath($fileName)
         ];
 
         $upload = $this->uploadFile($fileName, $page->id);
         $upload->assertStatus(200);
+
+        $attachment = Attachment::query()->orderBy('id', 'desc')->first();
+        $expectedResp['path'] = $attachment->path;
+
         $upload->assertJson($expectedResp);
         $this->assertDatabaseHas('attachments', $expectedResp);
 
         $this->deleteUploads();
     }
 
+    public function test_file_upload_does_not_use_filename()
+    {
+        $page = Page::first();
+        $fileName = 'upload_test_file.txt';
+
+
+        $upload = $this->asAdmin()->uploadFile($fileName, $page->id);
+        $upload->assertStatus(200);
+
+        $attachment = Attachment::query()->orderBy('id', 'desc')->first();
+        $this->assertNotContains($fileName, $attachment->path);
+        $this->assertStringEndsWith('.txt', $attachment->path);
+    }
+
     public function test_file_display_and_access()
     {
         $page = Page::first();
@@ -172,7 +179,8 @@ class AttachmentTest extends TestCase
         $fileName = 'deletion_test.txt';
         $this->uploadFile($fileName, $page->id);
 
-        $filePath = base_path('storage/' . $this->getUploadPath($fileName));
+        $attachment = Attachment::query()->orderBy('id', 'desc')->first();
+        $filePath = storage_path($attachment->path);
         $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
 
         $attachment = \BookStack\Uploads\Attachment::first();
@@ -193,7 +201,8 @@ class AttachmentTest extends TestCase
         $fileName = 'deletion_test.txt';
         $this->uploadFile($fileName, $page->id);
 
-        $filePath = base_path('storage/' . $this->getUploadPath($fileName));
+        $attachment = Attachment::query()->orderBy('id', 'desc')->first();
+        $filePath = storage_path($attachment->path);
 
         $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
         $this->assertDatabaseHas('attachments', [
index 4091bec570abd1d588599d619996d41129360ce3..8373a809c2962738a0895301aa64ed3ad4e5198f 100644 (file)
@@ -76,11 +76,23 @@ class ImageTest extends TestCase
         $upload->assertStatus(302);
 
         $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded php file was uploaded but should have been stopped');
+    }
 
-        $this->assertDatabaseMissing('images', [
-            'type' => 'gallery',
-            'name' => $fileName
-        ]);
+    public function test_files_with_double_extensions_cannot_be_uploaded()
+    {
+        $page = Page::first();
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+
+        $fileName = 'bad.phtml.png';
+        $relPath = $this->getTestImagePath('gallery', $fileName);
+        $this->deleteImage($relPath);
+
+        $file = $this->getTestImage($fileName);
+        $upload = $this->withHeader('Content-Type', 'image/png')->call('POST', '/images/gallery/upload', ['uploaded_to' => $page->id], [], ['file' => $file], []);
+        $upload->assertStatus(302);
+
+        $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded double extension file was uploaded but should have been stopped');
     }
 
     public function test_secure_images_uploads_to_correct_place()
diff --git a/tests/UserPreferencesTest.php b/tests/UserPreferencesTest.php
new file mode 100644 (file)
index 0000000..b816642
--- /dev/null
@@ -0,0 +1,76 @@
+<?php namespace Tests;
+
+class UserPreferencesTest extends TestCase
+{
+
+    public function test_update_sort_preference()
+    {
+        $editor = $this->getEditor();
+        $this->actingAs($editor);
+
+        $updateRequest = $this->patch('/settings/users/' . $editor->id.'/change-sort/books', [
+            'sort' => 'created_at',
+            'order' => 'desc'
+        ]);
+        $updateRequest->assertStatus(302);
+
+        $this->assertDatabaseHas('settings', [
+            'setting_key' => 'user:' . $editor->id . ':books_sort',
+            'value' => 'created_at'
+        ]);
+        $this->assertDatabaseHas('settings', [
+            'setting_key' => 'user:' . $editor->id . ':books_sort_order',
+            'value' => 'desc'
+        ]);
+        $this->assertEquals('created_at', setting()->getForCurrentUser('books_sort'));
+        $this->assertEquals('desc', setting()->getForCurrentUser('books_sort_order'));
+    }
+
+    public function test_update_sort_preference_defaults()
+    {
+        $editor = $this->getEditor();
+        $this->actingAs($editor);
+
+        $updateRequest = $this->patch('/settings/users/' . $editor->id.'/change-sort/bookshelves', [
+            'sort' => 'cat',
+            'order' => 'dog'
+        ]);
+        $updateRequest->assertStatus(302);
+
+        $this->assertEquals('name', setting()->getForCurrentUser('bookshelves_sort'));
+        $this->assertEquals('asc', setting()->getForCurrentUser('bookshelves_sort_order'));
+    }
+
+    public function test_update_sort_bad_entity_type_handled()
+    {
+        $editor = $this->getEditor();
+        $this->actingAs($editor);
+
+        $updateRequest = $this->patch('/settings/users/' . $editor->id.'/change-sort/dogs', [
+            'sort' => 'name',
+            'order' => 'asc'
+        ]);
+        $updateRequest->assertStatus(500);
+
+        $this->assertNotEmpty('name', setting()->getForCurrentUser('bookshelves_sort'));
+        $this->assertNotEmpty('asc', setting()->getForCurrentUser('bookshelves_sort_order'));
+    }
+
+    public function test_update_expansion_preference()
+    {
+        $editor = $this->getEditor();
+        $this->actingAs($editor);
+
+        $updateRequest = $this->patch('/settings/users/' . $editor->id.'/update-expansion-preference/home-details', ['expand' => 'true']);
+        $updateRequest->assertStatus(204);
+
+        $this->assertDatabaseHas('settings', [
+            'setting_key' => 'user:' . $editor->id . ':section_expansion#home-details',
+            'value' => 'true'
+        ]);
+        $this->assertEquals(true, setting()->getForCurrentUser('section_expansion#home-details'));
+
+        $invalidKeyRequest = $this->patch('/settings/users/' . $editor->id.'/update-expansion-preference/my-home-details', ['expand' => 'true']);
+        $invalidKeyRequest->assertStatus(500);
+    }
+}
\ No newline at end of file
index 0c29f63c5953d1752acbaeb6d890e83ad985d7ea..a7c7505a80db83867bdfb9c1636653589ccbe46a 100644 (file)
@@ -59,9 +59,9 @@ class UserProfileTest extends BrowserKitTest
         \Activity::add($entities['page'], 'page_create', $entities['book']->id);
 
         $this->asAdmin()->visit('/user/' . $newUser->id)
-            ->seeInElement('#recent-activity', 'updated book')
-            ->seeInElement('#recent-activity', 'created page')
-            ->seeInElement('#recent-activity', $entities['page']->name);
+            ->seeInElement('#recent-user-activity', 'updated book')
+            ->seeInElement('#recent-user-activity', 'created page')
+            ->seeInElement('#recent-user-activity', $entities['page']->name);
     }
 
     public function test_clicking_user_name_in_activity_leads_to_profile_page()
@@ -103,7 +103,7 @@ class UserProfileTest extends BrowserKitTest
         $this->actingAs($editor)
             ->visit('/books')
             ->pageNotHasElement('.featured-image-container')
-            ->pageHasElement('.content .entity-list-item');
+            ->pageHasElement('.content-wrap .entity-list-item');
     }
 
     public function test_books_view_is_grid()
diff --git a/tests/test-data/bad.phtml.png b/tests/test-data/bad.phtml.png
new file mode 100644 (file)
index 0000000..dd15f6e
Binary files /dev/null and b/tests/test-data/bad.phtml.png differ
index 5c0501e3ba627136916f1052e53f59e7bb30dc8a..4eae9b9be34683eec0f4efd08f039762e556c6ac 100644 (file)
@@ -2,7 +2,7 @@ const path = require('path');
 const dev = process.env.NODE_ENV !== 'production';
 
 const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
-const ExtractTextPlugin = require("extract-text-webpack-plugin");
+const MiniCssExtractPlugin = require("mini-css-extract-plugin");
 
 const config = {
     target: 'web',
@@ -35,12 +35,15 @@ const config = {
             },
             {
                 test: /\.scss$/,
-                use: ExtractTextPlugin.extract({
-                    fallback: "style-loader",
-                    use: [{
+                use: [
+                    {
+                        loader: MiniCssExtractPlugin.loader,
+                        options: {}
+                    },
+                    {
                         loader: "css-loader", options: {
-                            sourceMap: dev
-                        }
+                        sourceMap: dev
+                    }
                     }, {
                         loader: 'postcss-loader',
                         options: {
@@ -54,13 +57,15 @@ const config = {
                         loader: "sass-loader", options: {
                             sourceMap: dev
                         }
-                    }]
-                })
+                    }
+                ]
             }
         ]
     },
     plugins: [
-        new ExtractTextPlugin("[name].css"),
+        new MiniCssExtractPlugin({
+            filename: "[name].css",
+        }),
     ]
 };