]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' into release
authorDan Brown <redacted>
Sun, 20 Sep 2020 09:30:10 +0000 (10:30 +0100)
committerDan Brown <redacted>
Sun, 20 Sep 2020 09:30:10 +0000 (10:30 +0100)
396 files changed:
.env.example
.env.example.complete
.github/translators.txt
app/Actions/ActivityService.php
app/Actions/Tag.php
app/Actions/TagRepo.php
app/Auth/Access/ExternalAuthService.php
app/Auth/Access/RegistrationService.php
app/Auth/Access/Saml2Service.php
app/Auth/Permissions/JointPermission.php
app/Auth/Permissions/PermissionsRepo.php
app/Auth/Permissions/RolePermission.php
app/Auth/Role.php
app/Auth/User.php
app/Auth/UserRepo.php
app/Config/app.php
app/Config/logging.php
app/Config/saml2.php
app/Entities/Book.php
app/Entities/Bookshelf.php
app/Entities/Chapter.php
app/Entities/Entity.php
app/Entities/Managers/PageContent.php
app/Entities/Page.php
app/Entities/Repos/PageRepo.php
app/Entities/SearchOptions.php [new file with mode: 0644]
app/Entities/SearchService.php
app/Entities/SlugGenerator.php
app/Exceptions/Handler.php
app/Http/Controllers/Api/BookApiController.php [moved from app/Http/Controllers/Api/BooksApiController.php with 96% similarity]
app/Http/Controllers/Api/BookExportApiController.php [moved from app/Http/Controllers/Api/BooksExportApiController.php with 96% similarity]
app/Http/Controllers/Api/ChapterApiController.php [new file with mode: 0644]
app/Http/Controllers/Api/ChapterExportApiController.php [new file with mode: 0644]
app/Http/Controllers/AttachmentController.php
app/Http/Controllers/AuditLogController.php [new file with mode: 0644]
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/Images/DrawioImageController.php
app/Http/Controllers/Images/GalleryImageController.php
app/Http/Controllers/Images/ImageController.php
app/Http/Controllers/MaintenanceController.php [new file with mode: 0644]
app/Http/Controllers/PageController.php
app/Http/Controllers/PageRevisionController.php
app/Http/Controllers/PermissionController.php
app/Http/Controllers/SearchController.php
app/Http/Controllers/SettingController.php
app/Http/Controllers/TagController.php
app/Http/Controllers/UserController.php
app/Http/Middleware/ApiAuthenticate.php
app/Http/Middleware/Authenticate.php
app/Http/Middleware/Localization.php
app/Uploads/Attachment.php
app/Uploads/AttachmentService.php
app/Uploads/ImageRepo.php
app/Uploads/ImageService.php
app/helpers.php
composer.json
composer.lock
database/migrations/2020_08_04_111754_drop_joint_permissions_id.php [new file with mode: 0644]
database/migrations/2020_08_04_131052_remove_role_name_field.php [new file with mode: 0644]
database/migrations/2020_09_19_094251_add_activity_indexes.php [new file with mode: 0644]
dev/api/requests/chapters-create.json [new file with mode: 0644]
dev/api/requests/chapters-update.json [new file with mode: 0644]
dev/api/responses/books-read.json
dev/api/responses/chapters-create.json [new file with mode: 0644]
dev/api/responses/chapters-list.json [new file with mode: 0644]
dev/api/responses/chapters-read.json [new file with mode: 0644]
dev/api/responses/chapters-update.json [new file with mode: 0644]
dev/api/responses/shelves-read.json
dev/docs/components.md [new file with mode: 0644]
package-lock.json
package.json
phpcs.xml
phpunit.xml
readme.md
resources/js/components/add-remove-rows.js [new file with mode: 0644]
resources/js/components/ajax-delete-row.js [new file with mode: 0644]
resources/js/components/ajax-form.js [new file with mode: 0644]
resources/js/components/attachments.js [new file with mode: 0644]
resources/js/components/auto-suggest.js [new file with mode: 0644]
resources/js/components/code-editor.js [new file with mode: 0644]
resources/js/components/collapsible.js
resources/js/components/dropdown.js
resources/js/components/dropzone.js [new file with mode: 0644]
resources/js/components/entity-search.js [new file with mode: 0644]
resources/js/components/entity-selector-popup.js
resources/js/components/event-emit-select.js [new file with mode: 0644]
resources/js/components/image-manager.js [new file with mode: 0644]
resources/js/components/index.js
resources/js/components/markdown-editor.js
resources/js/components/optional-input.js [new file with mode: 0644]
resources/js/components/overlay.js [deleted file]
resources/js/components/page-comments.js
resources/js/components/page-editor.js [new file with mode: 0644]
resources/js/components/popup.js [new file with mode: 0644]
resources/js/components/sortable-list.js [new file with mode: 0644]
resources/js/components/submit-on-change.js [new file with mode: 0644]
resources/js/components/tabs.js [new file with mode: 0644]
resources/js/components/tag-manager.js [new file with mode: 0644]
resources/js/components/wysiwyg-editor.js
resources/js/index.js
resources/js/services/dom.js
resources/js/services/events.js
resources/js/services/http.js
resources/js/services/translations.js
resources/js/services/util.js
resources/js/vues/attachment-manager.js [deleted file]
resources/js/vues/code-editor.js [deleted file]
resources/js/vues/components/autosuggest.js [deleted file]
resources/js/vues/components/dropzone.js [deleted file]
resources/js/vues/entity-dashboard.js [deleted file]
resources/js/vues/image-manager.js [deleted file]
resources/js/vues/page-editor.js [deleted file]
resources/js/vues/search.js [deleted file]
resources/js/vues/tag-manager.js [deleted file]
resources/js/vues/vues.js [deleted file]
resources/lang/ar/activities.php
resources/lang/ar/common.php
resources/lang/ar/components.php
resources/lang/ar/entities.php
resources/lang/ar/errors.php
resources/lang/ar/settings.php
resources/lang/bg/activities.php [new file with mode: 0644]
resources/lang/bg/auth.php [new file with mode: 0644]
resources/lang/bg/common.php [new file with mode: 0644]
resources/lang/bg/components.php [new file with mode: 0644]
resources/lang/bg/entities.php [new file with mode: 0644]
resources/lang/bg/errors.php [new file with mode: 0644]
resources/lang/bg/pagination.php [new file with mode: 0644]
resources/lang/bg/passwords.php [new file with mode: 0644]
resources/lang/bg/settings.php [new file with mode: 0644]
resources/lang/bg/validation.php [new file with mode: 0644]
resources/lang/cs/activities.php
resources/lang/cs/auth.php
resources/lang/cs/common.php
resources/lang/cs/components.php
resources/lang/cs/entities.php
resources/lang/cs/errors.php
resources/lang/cs/pagination.php
resources/lang/cs/passwords.php
resources/lang/cs/settings.php
resources/lang/cs/validation.php
resources/lang/da/auth.php
resources/lang/da/common.php
resources/lang/da/components.php
resources/lang/da/entities.php
resources/lang/da/errors.php
resources/lang/da/pagination.php
resources/lang/da/passwords.php
resources/lang/da/settings.php
resources/lang/de/common.php
resources/lang/de/components.php
resources/lang/de/entities.php
resources/lang/de/errors.php
resources/lang/de/settings.php
resources/lang/de_informal/common.php
resources/lang/de_informal/components.php
resources/lang/de_informal/entities.php
resources/lang/de_informal/errors.php
resources/lang/de_informal/passwords.php
resources/lang/de_informal/settings.php
resources/lang/en/common.php
resources/lang/en/components.php
resources/lang/en/entities.php
resources/lang/en/errors.php
resources/lang/en/settings.php
resources/lang/es/common.php
resources/lang/es/components.php
resources/lang/es/entities.php
resources/lang/es/errors.php
resources/lang/es/settings.php
resources/lang/es_AR/auth.php
resources/lang/es_AR/common.php
resources/lang/es_AR/components.php
resources/lang/es_AR/entities.php
resources/lang/es_AR/errors.php
resources/lang/es_AR/passwords.php
resources/lang/es_AR/settings.php
resources/lang/fa/common.php
resources/lang/fa/components.php
resources/lang/fa/entities.php
resources/lang/fa/errors.php
resources/lang/fa/settings.php
resources/lang/fr/common.php
resources/lang/fr/components.php
resources/lang/fr/entities.php
resources/lang/fr/errors.php
resources/lang/fr/settings.php
resources/lang/he/common.php
resources/lang/he/components.php
resources/lang/he/entities.php
resources/lang/he/errors.php
resources/lang/he/settings.php
resources/lang/hu/common.php
resources/lang/hu/components.php
resources/lang/hu/entities.php
resources/lang/hu/errors.php
resources/lang/hu/settings.php
resources/lang/it/auth.php
resources/lang/it/common.php
resources/lang/it/components.php
resources/lang/it/entities.php
resources/lang/it/errors.php
resources/lang/it/settings.php
resources/lang/ja/activities.php
resources/lang/ja/auth.php
resources/lang/ja/common.php
resources/lang/ja/components.php
resources/lang/ja/entities.php
resources/lang/ja/errors.php
resources/lang/ja/settings.php
resources/lang/ko/activities.php
resources/lang/ko/auth.php
resources/lang/ko/common.php
resources/lang/ko/components.php
resources/lang/ko/entities.php
resources/lang/ko/errors.php
resources/lang/ko/passwords.php
resources/lang/ko/settings.php
resources/lang/nl/common.php
resources/lang/nl/components.php
resources/lang/nl/entities.php
resources/lang/nl/errors.php
resources/lang/nl/passwords.php
resources/lang/nl/settings.php
resources/lang/pl/auth.php
resources/lang/pl/common.php
resources/lang/pl/components.php
resources/lang/pl/entities.php
resources/lang/pl/errors.php
resources/lang/pl/passwords.php
resources/lang/pl/settings.php
resources/lang/pt/common.php
resources/lang/pt/components.php
resources/lang/pt/entities.php
resources/lang/pt/errors.php
resources/lang/pt/settings.php
resources/lang/pt_BR/auth.php
resources/lang/pt_BR/common.php
resources/lang/pt_BR/components.php
resources/lang/pt_BR/entities.php
resources/lang/pt_BR/errors.php
resources/lang/pt_BR/passwords.php
resources/lang/pt_BR/settings.php
resources/lang/ru/common.php
resources/lang/ru/components.php
resources/lang/ru/entities.php
resources/lang/ru/errors.php
resources/lang/ru/settings.php
resources/lang/sk/activities.php
resources/lang/sk/auth.php
resources/lang/sk/common.php
resources/lang/sk/components.php
resources/lang/sk/entities.php
resources/lang/sk/errors.php
resources/lang/sk/passwords.php
resources/lang/sk/settings.php
resources/lang/sl/common.php
resources/lang/sl/components.php
resources/lang/sl/entities.php
resources/lang/sl/errors.php
resources/lang/sl/settings.php
resources/lang/sv/common.php
resources/lang/sv/components.php
resources/lang/sv/entities.php
resources/lang/sv/errors.php
resources/lang/sv/settings.php
resources/lang/th/common.php
resources/lang/th/components.php
resources/lang/th/entities.php
resources/lang/th/errors.php
resources/lang/th/settings.php
resources/lang/tr/common.php
resources/lang/tr/components.php
resources/lang/tr/entities.php
resources/lang/tr/errors.php
resources/lang/tr/settings.php
resources/lang/uk/auth.php
resources/lang/uk/common.php
resources/lang/uk/components.php
resources/lang/uk/entities.php
resources/lang/uk/errors.php
resources/lang/uk/passwords.php
resources/lang/uk/settings.php
resources/lang/vi/common.php
resources/lang/vi/components.php
resources/lang/vi/entities.php
resources/lang/vi/errors.php
resources/lang/vi/settings.php
resources/lang/zh_CN/auth.php
resources/lang/zh_CN/common.php
resources/lang/zh_CN/components.php
resources/lang/zh_CN/entities.php
resources/lang/zh_CN/errors.php
resources/lang/zh_CN/passwords.php
resources/lang/zh_CN/settings.php
resources/lang/zh_TW/activities.php
resources/lang/zh_TW/auth.php
resources/lang/zh_TW/common.php
resources/lang/zh_TW/components.php
resources/lang/zh_TW/entities.php
resources/lang/zh_TW/errors.php
resources/lang/zh_TW/passwords.php
resources/lang/zh_TW/settings.php
resources/lang/zh_TW/validation.php
resources/sass/_blocks.scss
resources/sass/_colors.scss
resources/sass/_components.scss
resources/sass/_header.scss
resources/sass/_layout.scss
resources/sass/_lists.scss
resources/sass/_pages.scss
resources/sass/_spacing.scss
resources/sass/_text.scss
resources/sass/_tinymce.scss
resources/sass/styles.scss
resources/views/api-docs/index.blade.php
resources/views/attachments/list.blade.php [new file with mode: 0644]
resources/views/attachments/manager-edit-form.blade.php [new file with mode: 0644]
resources/views/attachments/manager-link-form.blade.php [new file with mode: 0644]
resources/views/attachments/manager-list.blade.php [new file with mode: 0644]
resources/views/attachments/manager.blade.php [new file with mode: 0644]
resources/views/books/form.blade.php
resources/views/books/show.blade.php
resources/views/chapters/form.blade.php
resources/views/chapters/show.blade.php
resources/views/comments/comment.blade.php
resources/views/comments/comments.blade.php
resources/views/comments/create.blade.php
resources/views/common/header.blade.php
resources/views/common/home.blade.php
resources/views/components/code-editor.blade.php
resources/views/components/dropzone.blade.php [new file with mode: 0644]
resources/views/components/entity-selector-popup.blade.php
resources/views/components/image-manager-form.blade.php [new file with mode: 0644]
resources/views/components/image-manager-list.blade.php [new file with mode: 0644]
resources/views/components/image-manager.blade.php
resources/views/components/tag-manager-list.blade.php [new file with mode: 0644]
resources/views/components/tag-manager.blade.php
resources/views/form/role-checkboxes.blade.php
resources/views/pages/attachment-manager.blade.php [deleted file]
resources/views/pages/edit.blade.php
resources/views/pages/editor-toolbox.blade.php
resources/views/pages/form.blade.php
resources/views/pages/markdown-editor.blade.php
resources/views/pages/page-display.blade.php
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/pages/wysiwyg-editor.blade.php
resources/views/partials/activity-item.blade.php
resources/views/partials/book-tree.blade.php
resources/views/partials/breadcrumb-listing.blade.php
resources/views/partials/entity-dashboard-search-box.blade.php [deleted file]
resources/views/partials/entity-dashboard-search-results.blade.php [deleted file]
resources/views/partials/entity-export-menu.blade.php
resources/views/partials/entity-search-form.blade.php [new file with mode: 0644]
resources/views/partials/entity-search-results.blade.php [new file with mode: 0644]
resources/views/partials/sort.blade.php
resources/views/search/all.blade.php
resources/views/search/form/boolean-filter.blade.php [new file with mode: 0644]
resources/views/search/form/date-filter.blade.php [new file with mode: 0644]
resources/views/search/form/term-list.blade.php [new file with mode: 0644]
resources/views/search/form/type-filter.blade.php [new file with mode: 0644]
resources/views/settings/audit.blade.php [new file with mode: 0644]
resources/views/settings/index.blade.php
resources/views/settings/navbar.blade.php
resources/views/settings/roles/delete.blade.php
resources/views/settings/roles/form.blade.php
resources/views/shelves/form.blade.php
routes/api.php
routes/web.php
tests/Api/ApiDocsTest.php
tests/Api/ChaptersApiTest.php [new file with mode: 0644]
tests/Api/TestsApi.php
tests/AuditLogTest.php [new file with mode: 0644]
tests/Auth/AuthTest.php
tests/Auth/LdapTest.php
tests/Auth/Saml2Test.php
tests/Entity/CommentTest.php
tests/Entity/EntitySearchTest.php
tests/Entity/EntityTest.php
tests/Entity/PageContentTest.php
tests/Entity/PageDraftTest.php
tests/Entity/PageRevisionTest.php
tests/Entity/SearchOptionsTest.php [new file with mode: 0644]
tests/Entity/TagTest.php
tests/ErrorTest.php
tests/Permissions/RolesTest.php
tests/SharedTestHelpers.php
tests/Unit/ConfigTest.php
tests/Unit/UrlTest.php
tests/Uploads/AttachmentTest.php
tests/Uploads/ImageTest.php
tests/Uploads/UsesImages.php
version
webpack.config.js [deleted file]

index f5e81277cedbdc90bb6a896b4c1476f16154c95a..47f2367b08a9a3d88935d757b78acdb5e879c988 100644 (file)
@@ -1,3 +1,11 @@
+# This file, when named as ".env" in the root of your BookStack install
+# folder, is used for the core configuration of the application.
+# By default this file contains the most common required options but
+# a full list of options can be found in the '.env.example.complete' file.
+
+# NOTE: If any of your values contain a space or a hash you will need to
+# wrap the entire value in quotes. (eg. MAIL_FROM_NAME="BookStack Mailer")
+
 # Application key
 # Used for encryption where needed.
 # Run `php artisan key:generate` to generate a valid key.
@@ -5,7 +13,7 @@ APP_KEY=SomeRandomString
 
 # Application URL
 # Remove the hash below and set a URL if using BookStack behind
-# a proxy, if using a third-party authentication option.
+# a proxy or if using a third-party authentication option.
 # This must be the root URL that you want to host BookStack on.
 # All URL's in BookStack will be generated using this value.
 #APP_URL=https://example.com
@@ -25,11 +33,10 @@ MAIL_FROM_NAME=BookStack
 
 # SMTP mail options
+# These settings can be checked using the "Send a Test Email"
+# feature found in the "Settings > Maintenance" area of the system.
 MAIL_HOST=localhost
 MAIL_PORT=1025
 MAIL_USERNAME=null
 MAIL_PASSWORD=null
-MAIL_ENCRYPTION=null
-
-
-# A full list of options can be found in the '.env.example.complete' file.
\ No newline at end of file
+MAIL_ENCRYPTION=null
\ No newline at end of file
index 472ca051b336388715aab7781c8ec7ab5658978c..39e7b43602b9da60820e1ef767640faec5393513 100644 (file)
@@ -270,4 +270,12 @@ API_DEFAULT_ITEM_COUNT=100
 API_MAX_ITEM_COUNT=500
 
 # The number of API requests that can be made per minute by a single user.
-API_REQUESTS_PER_MIN=180
\ No newline at end of file
+API_REQUESTS_PER_MIN=180
+
+# Enable the logging of failed email+password logins with the given message.
+# The default log channel below uses the php 'error_log' function which commonly
+# results in messages being output to the webserver error logs.
+# The message can contain a %u parameter which will be replaced with the login
+# user identifier (Username or email).
+LOG_FAILED_LOGIN_MESSAGE=false
+LOG_FAILED_LOGIN_CHANNEL=errorlog_plain_webserver
index 098751fcd85030fd3d063bfda55081e548f90045..e9ea8ffbacf256c773d90d263986eb66030e7417 100644 (file)
@@ -61,7 +61,7 @@ Rodrigo Saczuk Niz (rodrigoniz) :: Portuguese, Brazilian
 aekramer :: Dutch
 JachuPL :: Polish
 milesteg :: Hungarian
-Beenbag :: German
+Beenbag :: German; German Informal
 Lett3rs :: Danish
 Julian (julian.henneberg) :: German; German Informal
 3GNWn :: Danish
@@ -98,3 +98,25 @@ Thinkverse (thinkverse) :: Swedish
 alef (toishoki) :: Turkish
 Robbert Feunekes (Muukuro) :: Dutch
 seohyeon.joo :: Korean
+Orenda (OREDNA) :: Bulgarian
+Marek Pavelka (marapavelka) :: Czech
+Venkinovec :: Czech
+Tommy Ku (tommyku) :: Chinese Traditional; Japanese
+Michał Bielejewski  (bielej) :: Polish
+jozefrebjak :: Slovak
+Ikhwan Koo (Ikhwan.Koo) :: Korean
+Whay (remkovdhoef) :: Dutch
+jc7115 :: Chinese Traditional
+주서현 (seohyeon.joo) :: Korean
+ReadySystems :: Arabic
+HFinch :: German; German Informal
+brechtgijsens :: Dutch
+Lowkey (v587ygq) :: Chinese Simplified
+sdl-blue :: German Informal
+sqlik :: Polish
+Roy van Schaijk (royvanschaijk) :: Dutch
+Simsimpicpic :: French
+Zenahr Barzani (Zenahr) :: German; Japanese; Dutch; German Informal
+tatsuya.info :: Japanese
+fadiapp :: Arabic
+Jakub “Jéžiš” Bouček (jakubboucek) :: Czech
index 9b69cbb1747662240d4c4055f8647f58a17bdb9b..e6b004f01fb0bc15d3b892ba6f56c49676e31cdc 100644 (file)
@@ -4,6 +4,7 @@ use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Auth\User;
 use BookStack\Entities\Entity;
 use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Log;
 
 class ActivityService
 {
@@ -49,7 +50,7 @@ class ActivityService
     protected function newActivityForUser(string $key, ?int $bookId = null): Activity
     {
         return $this->activity->newInstance()->forceFill([
-            'key' => strtolower($key),
+            'key'     => strtolower($key),
             'user_id' => $this->user->id,
             'book_id' => $bookId ?? 0,
         ]);
@@ -64,8 +65,8 @@ class ActivityService
     {
         $activities = $entity->activity()->get();
         $entity->activity()->update([
-            'extra' => $entity->name,
-            'entity_id' => 0,
+            'extra'       => $entity->name,
+            'entity_id'   => 0,
             'entity_type' => '',
         ]);
         return $activities;
@@ -99,7 +100,7 @@ class ActivityService
             $query = $this->activity->newQuery()->where('entity_type', '=', $entity->getMorphClass())
                 ->where('entity_id', '=', $entity->id);
         }
-        
+
         $activity = $this->permissionService
             ->filterRestrictedEntityRelations($query, 'activities', 'entity_id', 'entity_type')
             ->orderBy('created_at', 'desc')
@@ -159,4 +160,20 @@ class ActivityService
             session()->flash('success', $message);
         }
     }
+
+    /**
+     * Log out a failed login attempt, Providing the given username
+     * as part of the message if the '%u' string is used.
+     */
+    public function logFailedLogin(string $username)
+    {
+        $message = config('logging.failed_login.message');
+        if (!$message) {
+            return;
+        }
+
+        $message = str_replace("%u", $username, $message);
+        $channel = config('logging.failed_login.channel');
+        Log::channel($channel)->warning($message);
+    }
 }
index 38d0458e46425f53fd13c6421d745c8fe67b0101..80a91150868e9cd87be62685891237c392606328 100644 (file)
@@ -9,6 +9,7 @@ use BookStack\Model;
 class Tag extends Model
 {
     protected $fillable = ['name', 'value', 'order'];
+    protected $hidden = ['id', 'entity_id', 'entity_type'];
 
     /**
      * Get the entity that this tag belongs to
index 0cbfa4163bf9120226aafea71e678a4f13b920a8..0297d8bc6997b790085a485b1761085cc946ce59 100644 (file)
@@ -2,71 +2,31 @@
 
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Entities\Entity;
+use DB;
+use Illuminate\Support\Collection;
 
-/**
- * Class TagRepo
- * @package BookStack\Repos
- */
 class TagRepo
 {
 
     protected $tag;
-    protected $entity;
     protected $permissionService;
 
     /**
      * TagRepo constructor.
-     * @param \BookStack\Actions\Tag $attr
-     * @param \BookStack\Entities\Entity $ent
-     * @param \BookStack\Auth\Permissions\PermissionService $ps
      */
-    public function __construct(Tag $attr, Entity $ent, PermissionService $ps)
+    public function __construct(Tag $tag, PermissionService $ps)
     {
-        $this->tag = $attr;
-        $this->entity = $ent;
+        $this->tag = $tag;
         $this->permissionService = $ps;
     }
 
-    /**
-     * Get an entity instance of its particular type.
-     * @param $entityType
-     * @param $entityId
-     * @param string $action
-     * @return \Illuminate\Database\Eloquent\Model|null|static
-     */
-    public function getEntity($entityType, $entityId, $action = 'view')
-    {
-        $entityInstance = $this->entity->getEntityInstance($entityType);
-        $searchQuery = $entityInstance->where('id', '=', $entityId)->with('tags');
-        $searchQuery = $this->permissionService->enforceEntityRestrictions($entityType, $searchQuery, $action);
-        return $searchQuery->first();
-    }
-
-    /**
-     * Get all tags for a particular entity.
-     * @param string $entityType
-     * @param int $entityId
-     * @return mixed
-     */
-    public function getForEntity($entityType, $entityId)
-    {
-        $entity = $this->getEntity($entityType, $entityId);
-        if ($entity === null) {
-            return collect();
-        }
-
-        return $entity->tags;
-    }
-
     /**
      * Get tag name suggestions from scanning existing tag names.
      * If no search term is given the 50 most popular tag names are provided.
-     * @param $searchTerm
-     * @return array
      */
-    public function getNameSuggestions($searchTerm = false)
+    public function getNameSuggestions(?string $searchTerm): Collection
     {
-        $query = $this->tag->select('*', \DB::raw('count(*) as count'))->groupBy('name');
+        $query = $this->tag->select('*', DB::raw('count(*) as count'))->groupBy('name');
 
         if ($searchTerm) {
             $query = $query->where('name', 'LIKE', $searchTerm . '%')->orderBy('name', 'desc');
@@ -82,13 +42,10 @@ class TagRepo
      * Get tag value suggestions from scanning existing tag values.
      * If no search is given the 50 most popular values are provided.
      * Passing a tagName will only find values for a tags with a particular name.
-     * @param $searchTerm
-     * @param $tagName
-     * @return array
      */
-    public function getValueSuggestions($searchTerm = false, $tagName = false)
+    public function getValueSuggestions(?string $searchTerm, ?string $tagName): Collection
     {
-        $query = $this->tag->select('*', \DB::raw('count(*) as count'))->groupBy('value');
+        $query = $this->tag->select('*', DB::raw('count(*) as count'))->groupBy('value');
 
         if ($searchTerm) {
             $query = $query->where('value', 'LIKE', $searchTerm . '%')->orderBy('value', 'desc');
@@ -96,7 +53,7 @@ class TagRepo
             $query = $query->orderBy('count', 'desc')->take(50);
         }
 
-        if ($tagName !== false) {
+        if ($tagName) {
             $query = $query->where('name', '=', $tagName);
         }
 
@@ -106,35 +63,28 @@ class TagRepo
 
     /**
      * Save an array of tags to an entity
-     * @param \BookStack\Entities\Entity $entity
-     * @param array $tags
-     * @return array|\Illuminate\Database\Eloquent\Collection
      */
-    public function saveTagsToEntity(Entity $entity, $tags = [])
+    public function saveTagsToEntity(Entity $entity, array $tags = []): iterable
     {
         $entity->tags()->delete();
-        $newTags = [];
-        foreach ($tags as $tag) {
-            if (trim($tag['name']) === '') {
-                continue;
-            }
-            $newTags[] = $this->newInstanceFromInput($tag);
-        }
+
+        $newTags = collect($tags)->filter(function ($tag) {
+            return boolval(trim($tag['name']));
+        })->map(function ($tag) {
+            return $this->newInstanceFromInput($tag);
+        })->all();
 
         return $entity->tags()->saveMany($newTags);
     }
 
     /**
      * Create a new Tag instance from user input.
-     * @param $input
-     * @return \BookStack\Actions\Tag
+     * Input must be an array with a 'name' and an optional 'value' key.
      */
-    protected function newInstanceFromInput($input)
+    protected function newInstanceFromInput(array $input): Tag
     {
         $name = trim($input['name']);
         $value = isset($input['value']) ? trim($input['value']) : '';
-        // Any other modification or cleanup required can go here
-        $values = ['name' => $name, 'value' => $value];
-        return $this->tag->newInstance($values);
+        return $this->tag->newInstance(['name' => $name, 'value' => $value]);
     }
 }
index db8bd2dfb7c2f83cd855d4622b818f17581045f8..4c71db21adff7a559b929353ec4974512c16df14 100644 (file)
@@ -3,6 +3,8 @@
 use BookStack\Auth\Role;
 use BookStack\Auth\User;
 use Illuminate\Database\Eloquent\Builder;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\DB;
 
 class ExternalAuthService
 {
@@ -39,22 +41,14 @@ class ExternalAuthService
     /**
      * Match an array of group names to BookStack system roles.
      * Formats group names to be lower-case and hyphenated.
-     * @param array $groupNames
-     * @return \Illuminate\Support\Collection
      */
-    protected function matchGroupsToSystemsRoles(array $groupNames)
+    protected function matchGroupsToSystemsRoles(array $groupNames): Collection
     {
         foreach ($groupNames as $i => $groupName) {
             $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
         }
 
-        $roles = Role::query()->where(function (Builder $query) use ($groupNames) {
-            $query->whereIn('name', $groupNames);
-            foreach ($groupNames as $groupName) {
-                $query->orWhere('external_auth_id', 'LIKE', '%' . $groupName . '%');
-            }
-        })->get();
-
+        $roles = Role::query()->get(['id', 'external_auth_id', 'display_name']);
         $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
             return $this->roleMatchesGroupNames($role, $groupNames);
         });
index 9136b37b5a86ea49f1f0a8ccaac27b1fd25eb27d..b85f7ffd83c24a0aa84b97997ee75f41735fc3b6 100644 (file)
@@ -71,15 +71,15 @@ class RegistrationService
         // Start email confirmation flow if required
         if ($this->emailConfirmationService->confirmationRequired() && !$emailConfirmed) {
             $newUser->save();
-            $message = '';
 
             try {
                 $this->emailConfirmationService->sendConfirmation($newUser);
+                session()->flash('sent-email-confirmation', true);
             } catch (Exception $e) {
                 $message = trans('auth.email_confirm_send_error');
+                throw new UserRegistrationException($message, '/register/confirm');
             }
 
-            throw new UserRegistrationException($message, '/register/confirm');
         }
 
         return $newUser;
index 8f9a24cdeacd142106c0a92411962bd59de877c5..89ddd0011ecb037c8831b4a79260a18030ee7abe 100644 (file)
@@ -311,7 +311,6 @@ class Saml2Service extends ExternalAuthService
 
     /**
      * Get the user from the database for the specified details.
-     * @throws SamlException
      * @throws UserRegistrationException
      */
     protected function getOrRegisterUser(array $userDetails): ?User
index c48549b8f00c5cac2e9996fa9c4c4e0d483af365..8d1776bd8a45ad59d74d417a82b370ccf20b1fa0 100644 (file)
@@ -3,25 +3,26 @@
 use BookStack\Auth\Role;
 use BookStack\Entities\Entity;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Database\Eloquent\Relations\MorphOne;
 
 class JointPermission extends Model
 {
+    protected $primaryKey = null;
     public $timestamps = false;
 
     /**
      * Get the role that this points to.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function role()
+    public function role(): BelongsTo
     {
         return $this->belongsTo(Role::class);
     }
 
     /**
      * Get the entity this points to.
-     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
      */
-    public function entity()
+    public function entity(): MorphOne
     {
         return $this->morphOne(Entity::class, 'entity');
     }
index 56ef193015f7103a98bb440ec60498e5bf765c25..ce61093cc5ed26d4f81ebb01671e4a1e91eac6e1 100644 (file)
@@ -1,8 +1,9 @@
 <?php namespace BookStack\Auth\Permissions;
 
-use BookStack\Auth\Permissions;
 use BookStack\Auth\Role;
 use BookStack\Exceptions\PermissionsException;
+use Exception;
+use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Support\Str;
 
 class PermissionsRepo
@@ -16,11 +17,8 @@ class PermissionsRepo
 
     /**
      * PermissionsRepo constructor.
-     * @param RolePermission $permission
-     * @param Role $role
-     * @param \BookStack\Auth\Permissions\PermissionService $permissionService
      */
-    public function __construct(RolePermission $permission, Role $role, Permissions\PermissionService $permissionService)
+    public function __construct(RolePermission $permission, Role $role, PermissionService $permissionService)
     {
         $this->permission = $permission;
         $this->role = $role;
@@ -29,46 +27,34 @@ class PermissionsRepo
 
     /**
      * Get all the user roles from the system.
-     * @return \Illuminate\Database\Eloquent\Collection|static[]
      */
-    public function getAllRoles()
+    public function getAllRoles(): Collection
     {
         return $this->role->all();
     }
 
     /**
      * Get all the roles except for the provided one.
-     * @param Role $role
-     * @return mixed
      */
-    public function getAllRolesExcept(Role $role)
+    public function getAllRolesExcept(Role $role): Collection
     {
         return $this->role->where('id', '!=', $role->id)->get();
     }
 
     /**
      * Get a role via its ID.
-     * @param $id
-     * @return mixed
      */
-    public function getRoleById($id)
+    public function getRoleById($id): Role
     {
-        return $this->role->findOrFail($id);
+        return $this->role->newQuery()->findOrFail($id);
     }
 
     /**
      * Save a new role into the system.
-     * @param array $roleData
-     * @return Role
      */
-    public function saveNewRole($roleData)
+    public function saveNewRole(array $roleData): Role
     {
         $role = $this->role->newInstance($roleData);
-        $role->name = str_replace(' ', '-', strtolower($roleData['display_name']));
-        // Prevent duplicate names
-        while ($this->role->where('name', '=', $role->name)->count() > 0) {
-            $role->name .= strtolower(Str::random(2));
-        }
         $role->save();
 
         $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
@@ -80,13 +66,11 @@ class PermissionsRepo
     /**
      * Updates an existing role.
      * Ensure Admin role always have core permissions.
-     * @param $roleId
-     * @param $roleData
-     * @throws PermissionsException
      */
-    public function updateRole($roleId, $roleData)
+    public function updateRole($roleId, array $roleData)
     {
-        $role = $this->role->findOrFail($roleId);
+        /** @var Role $role */
+        $role = $this->role->newQuery()->findOrFail($roleId);
 
         $permissions = isset($roleData['permissions']) ? array_keys($roleData['permissions']) : [];
         if ($role->system_name === 'admin') {
@@ -108,16 +92,19 @@ class PermissionsRepo
 
     /**
      * Assign an list of permission names to an role.
-     * @param Role $role
-     * @param array $permissionNameArray
      */
-    public function assignRolePermissions(Role $role, $permissionNameArray = [])
+    public function assignRolePermissions(Role $role, array $permissionNameArray = [])
     {
         $permissions = [];
         $permissionNameArray = array_values($permissionNameArray);
-        if ($permissionNameArray && count($permissionNameArray) > 0) {
-            $permissions = $this->permission->whereIn('name', $permissionNameArray)->pluck('id')->toArray();
+
+        if ($permissionNameArray) {
+            $permissions = $this->permission->newQuery()
+                ->whereIn('name', $permissionNameArray)
+                ->pluck('id')
+                ->toArray();
         }
+
         $role->permissions()->sync($permissions);
     }
 
@@ -126,13 +113,13 @@ class PermissionsRepo
      * Check it's not an admin role or set as default before deleting.
      * If an migration Role ID is specified the users assign to the current role
      * will be added to the role of the specified id.
-     * @param $roleId
-     * @param $migrateRoleId
      * @throws PermissionsException
+     * @throws Exception
      */
     public function deleteRole($roleId, $migrateRoleId)
     {
-        $role = $this->role->findOrFail($roleId);
+        /** @var Role $role */
+        $role = $this->role->newQuery()->findOrFail($roleId);
 
         // Prevent deleting admin role or default registration role.
         if ($role->system_name && in_array($role->system_name, $this->systemRoles)) {
@@ -142,9 +129,9 @@ class PermissionsRepo
         }
 
         if ($migrateRoleId) {
-            $newRole = $this->role->find($migrateRoleId);
+            $newRole = $this->role->newQuery()->find($migrateRoleId);
             if ($newRole) {
-                $users = $role->users->pluck('id')->toArray();
+                $users = $role->users()->pluck('id')->toArray();
                 $newRole->users()->sync($users);
             }
         }
index 8b07b3073bf9add0eb1f4a216844c69e05235832..7f44ff8152b5a23b759b5fce3a83238da01855f9 100644 (file)
@@ -3,6 +3,9 @@
 use BookStack\Auth\Role;
 use BookStack\Model;
 
+/**
+ * @property int $id
+ */
 class RolePermission extends Model
 {
     /**
index df9b1cea93faa7d413800e966248fd6f99eb62b1..13ec6df16b8488c23120d9a129e617fcad99182b 100644 (file)
@@ -3,13 +3,16 @@
 use BookStack\Auth\Permissions\JointPermission;
 use BookStack\Auth\Permissions\RolePermission;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Collection;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 
 /**
  * Class Role
+ * @property int $id
  * @property string $display_name
  * @property string $description
  * @property string $external_auth_id
- * @package BookStack\Auth
+ * @property string $system_name
  */
 class Role extends Model
 {
@@ -26,9 +29,8 @@ class Role extends Model
 
     /**
      * Get all related JointPermissions.
-     * @return \Illuminate\Database\Eloquent\Relations\HasMany
      */
-    public function jointPermissions()
+    public function jointPermissions(): HasMany
     {
         return $this->hasMany(JointPermission::class);
     }
@@ -43,10 +45,8 @@ class Role extends Model
 
     /**
      * Check if this role has a permission.
-     * @param $permissionName
-     * @return bool
      */
-    public function hasPermission($permissionName)
+    public function hasPermission(string $permissionName): bool
     {
         $permissions = $this->getRelationValue('permissions');
         foreach ($permissions as $permission) {
@@ -59,7 +59,6 @@ class Role extends Model
 
     /**
      * Add a permission to this role.
-     * @param RolePermission $permission
      */
     public function attachPermission(RolePermission $permission)
     {
@@ -68,7 +67,6 @@ class Role extends Model
 
     /**
      * Detach a single permission from this role.
-     * @param RolePermission $permission
      */
     public function detachPermission(RolePermission $permission)
     {
@@ -76,39 +74,33 @@ class Role extends Model
     }
 
     /**
-     * Get the role object for the specified role.
-     * @param $roleName
-     * @return Role
+     * Get the role of the specified display name.
      */
-    public static function getRole($roleName)
+    public static function getRole(string $displayName): ?Role
     {
-        return static::query()->where('name', '=', $roleName)->first();
+        return static::query()->where('display_name', '=', $displayName)->first();
     }
 
     /**
      * Get the role object for the specified system role.
-     * @param $roleName
-     * @return Role
      */
-    public static function getSystemRole($roleName)
+    public static function getSystemRole(string $systemName): ?Role
     {
-        return static::query()->where('system_name', '=', $roleName)->first();
+        return static::query()->where('system_name', '=', $systemName)->first();
     }
 
     /**
      * Get all visible roles
-     * @return mixed
      */
-    public static function visible()
+    public static function visible(): Collection
     {
         return static::query()->where('hidden', '=', false)->orderBy('name')->get();
     }
 
     /**
      * Get the roles that can be restricted.
-     * @return \Illuminate\Database\Eloquent\Builder[]|\Illuminate\Database\Eloquent\Collection
      */
-    public static function restrictable()
+    public static function restrictable(): Collection
     {
         return static::query()->where('system_name', '!=', 'admin')->get();
     }
index a581d999340b1d76294a0c2dbb0a55c4501f6190..f65ef5316f67bfe3c68f40a6e2f7925ac7aa8f27 100644 (file)
@@ -49,7 +49,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
      */
     protected $hidden = [
         'password', 'remember_token', 'system_name', 'email_confirmed', 'external_auth_id', 'email',
-        'created_at', 'updated_at',
+        'created_at', 'updated_at', 'image_id',
     ];
 
     /**
@@ -101,12 +101,10 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
     /**
      * Check if the user has a role.
-     * @param $role
-     * @return mixed
      */
-    public function hasRole($role)
+    public function hasRole($roleId): bool
     {
-        return $this->roles->pluck('name')->contains($role);
+        return $this->roles->pluck('id')->contains($roleId);
     }
 
     /**
@@ -163,7 +161,6 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
 
     /**
      * Attach a role to this user.
-     * @param Role $role
      */
     public function attachRole(Role $role)
     {
index cfa7bfce1f1c836ffde1beddd0f58e66d07bd99a..fdb8c0923882cabe08e03f4db68e6b89609e1468 100644 (file)
@@ -238,7 +238,7 @@ class UserRepo
      */
     public function getAllRoles()
     {
-        return $this->role->newQuery()->orderBy('name', 'asc')->get();
+        return $this->role->newQuery()->orderBy('display_name', 'asc')->get();
     }
 
     /**
index a9956edd842a1afc6d9fefe83a5a1ccab43976e9..8a1d175284851708caa41dff5e44ef6b4cb8a1a1 100755 (executable)
@@ -52,7 +52,7 @@ return [
     'locale' => env('APP_LANG', 'en'),
 
     // Locales available
-    'locales' => ['en', 'ar', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'it', 'ja', 'ko', 'nl', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl',  'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
+    'locales' => ['en', 'ar', 'bg', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'it', 'ja', 'ko', 'nl', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl',  'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
 
     //  Application Fallback Locale
     'fallback_locale' => 'en',
index 0b55dc24db921f55bb01460fe249560349736c4f..afd56e48256e492a069e1ea996942c6d73940e2e 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+use Monolog\Formatter\LineFormatter;
+use Monolog\Handler\ErrorLogHandler;
 use Monolog\Handler\NullHandler;
 use Monolog\Handler\StreamHandler;
 
@@ -73,10 +75,38 @@ return [
             'level' => 'debug',
         ],
 
+        // Custom errorlog implementation that logs out a plain,
+        // non-formatted message intended for the webserver log.
+        'errorlog_plain_webserver' => [
+            'driver' => 'monolog',
+            'level' => 'debug',
+            'handler' => ErrorLogHandler::class,
+            'handler_with' => [4],
+            'formatter' => LineFormatter::class,
+            'formatter_with' => [
+                'format' => "%message%",
+            ],
+        ],
+
         'null' => [
             'driver' => 'monolog',
             'handler' => NullHandler::class,
         ],
+
+        // Testing channel
+        // Uses a shared testing instance during tests
+        // so that logs can be checked against.
+        'testing' => [
+            'driver' => 'testing',
+        ],
+    ],
+
+
+    // Failed Login Message
+    // Allows a configurable message to be logged when a login request fails.
+    'failed_login' => [
+        'message' => env('LOG_FAILED_LOGIN_MESSAGE', null),
+        'channel' => env('LOG_FAILED_LOGIN_CHANNEL', 'errorlog_plain_webserver'),
     ],
 
 ];
index 5f2c1395b836aacedd36496d153a0ea52f412165..d695abf325d1e5260b14d8325ea503c18bfe1abb 100644 (file)
@@ -101,7 +101,7 @@ return [
                 'url' => env('SAML2_IDP_SLO', null),
                 // URL location of the IdP where the SP will send the SLO Response (ResponseLocation)
                 // if not set, url for the SLO Request will be used
-                'responseUrl' => '',
+                'responseUrl' => null,
                 // SAML protocol binding to be used when returning the <Response>
                 // message.  Onelogin Toolkit supports for this endpoint the
                 // HTTP-Redirect binding only
index 38b7d4a8c29d3361652192d703a8362029bd62e4..af8344b88f5cb440b9abeb6913e3e55ffeda68f6 100644 (file)
@@ -19,7 +19,7 @@ class Book extends Entity implements HasCoverImage
     public $searchFactor = 2;
 
     protected $fillable = ['name', 'description'];
-    protected $hidden = ['restricted', 'pivot'];
+    protected $hidden = ['restricted', 'pivot', 'image_id'];
 
     /**
      * Get the url for this book.
index c7ba840e0ce0803f5e2da79ddf177bc1bb0dc563..474ba51cd8204bf27dfc95ad421029cdaa8e7375 100644 (file)
@@ -12,7 +12,7 @@ class Bookshelf extends Entity implements HasCoverImage
 
     protected $fillable = ['name', 'description', 'image_id'];
 
-    protected $hidden = ['restricted'];
+    protected $hidden = ['restricted', 'image_id'];
 
     /**
      * Get the books in this shelf.
index 848bc6448bd467b853ef25c8982c0aa8b5ad6e4c..3290afcfa6b90b0b01b653f99dfd7f93598f6556 100644 (file)
@@ -12,6 +12,7 @@ class Chapter extends BookChild
     public $searchFactor = 1.3;
 
     protected $fillable = ['name', 'description', 'priority', 'book_id'];
+    protected $hidden = ['restricted', 'pivot'];
 
     /**
      * Get the pages that this chapter contains.
index 5013c39cfcf2bf309f1931594ebe702dd00dc44c..120290d8ff12317529bd9d60d05519189098d0c8 100644 (file)
@@ -238,10 +238,8 @@ class Entity extends Ownable
 
     /**
      * Gets a limited-length version of the entities name.
-     * @param int $length
-     * @return string
      */
-    public function getShortName($length = 25)
+    public function getShortName(int $length = 25): string
     {
         if (mb_strlen($this->name) <= $length) {
             return $this->name;
@@ -288,7 +286,7 @@ class Entity extends Ownable
     public function rebuildPermissions()
     {
         /** @noinspection PhpUnhandledExceptionInspection */
-        Permissions::buildJointPermissionsForEntity($this);
+        Permissions::buildJointPermissionsForEntity(clone $this);
     }
 
     /**
@@ -297,7 +295,7 @@ class Entity extends Ownable
     public function indexForSearch()
     {
         $searchService = app()->make(SearchService::class);
-        $searchService->indexEntity($this);
+        $searchService->indexEntity(clone $this);
     }
 
     /**
index 36bc2445c33caeb015b7cf6acd847d8fe6eb0fa0..e417b1caad6e2b07bb457889c3b1489354e942ad 100644 (file)
@@ -108,7 +108,7 @@ class PageContent
     protected function toPlainText(): string
     {
         $html = $this->render(true);
-        return strip_tags($html);
+        return html_entity_decode(strip_tags($html));
     }
 
     /**
index 76dc628fbf3f59e0bbbcf98897e715b743638213..32ba2981d807e7a2b2b7a35c7984e523a82448ca 100644 (file)
@@ -21,12 +21,14 @@ use Permissions;
  */
 class Page extends BookChild
 {
-    protected $fillable = ['name', 'html', 'priority', 'markdown'];
+    protected $fillable = ['name', 'priority', 'markdown'];
 
     protected $simpleAttributes = ['name', 'id', 'slug'];
 
     public $textField = 'text';
 
+    protected $hidden = ['html', 'markdown', 'text', 'restricted', 'pivot'];
+
     /**
      * Get the entities that are visible to the current user.
      */
index e49eeb1ef5518eb8aa124d11e981236f11ebbe04..e5f13463c388f781dd215338afb4af37cefaa7c5 100644 (file)
@@ -180,12 +180,11 @@ class PageRepo
             $page->template = ($input['template'] === 'true');
         }
 
+        $pageContent = new PageContent($page);
+        $pageContent->setNewHTML($input['html']);
         $this->baseRepo->update($page, $input);
 
         // Update with new details
-        $page->fill($input);
-        $pageContent = new PageContent($page);
-        $pageContent->setNewHTML($input['html']);
         $page->revision_count++;
 
         if (setting('app-editor') !== 'markdown') {
@@ -211,7 +210,7 @@ class PageRepo
      */
     protected function savePageRevision(Page $page, string $summary = null)
     {
-        $revision = new PageRevision($page->toArray());
+        $revision = new PageRevision($page->getAttributes());
 
         if (setting('app-editor') !== 'markdown') {
             $revision->markdown = '';
@@ -279,7 +278,7 @@ class PageRepo
         $revision = $page->revisions()->where('id', '=', $revisionId)->first();
         $page->fill($revision->toArray());
         $content = new PageContent($page);
-        $content->setNewHTML($page->html);
+        $content->setNewHTML($revision->html);
         $page->updated_by = user()->id;
         $page->refreshSlug();
         $page->save();
diff --git a/app/Entities/SearchOptions.php b/app/Entities/SearchOptions.php
new file mode 100644 (file)
index 0000000..a121bd7
--- /dev/null
@@ -0,0 +1,141 @@
+<?php namespace BookStack\Entities;
+
+use Illuminate\Http\Request;
+
+class SearchOptions
+{
+
+    /**
+     * @var array
+     */
+    public $searches = [];
+
+    /**
+     * @var array
+     */
+    public $exacts = [];
+
+    /**
+     * @var array
+     */
+    public $tags = [];
+
+    /**
+     * @var array
+     */
+    public $filters = [];
+
+    /**
+     * Create a new instance from a search string.
+     */
+    public static function fromString(string $search): SearchOptions
+    {
+        $decoded = static::decode($search);
+        $instance = new static();
+        foreach ($decoded as $type => $value) {
+            $instance->$type = $value;
+        }
+        return $instance;
+    }
+
+    /**
+     * Create a new instance from a request.
+     * Will look for a classic string term and use that
+     * Otherwise we'll use the details from an advanced search form.
+     */
+    public static function fromRequest(Request $request): SearchOptions
+    {
+        if (!$request->has('search') && !$request->has('term')) {
+            return static::fromString('');
+        }
+
+        if ($request->has('term')) {
+            return static::fromString($request->get('term'));
+        }
+
+        $instance = new static();
+        $inputs = $request->only(['search', 'types', 'filters', 'exact', 'tags']);
+        $instance->searches = explode(' ', $inputs['search'] ?? []);
+        $instance->exacts = array_filter($inputs['exact'] ?? []);
+        $instance->tags = array_filter($inputs['tags'] ?? []);
+        foreach (($inputs['filters'] ?? []) as $filterKey => $filterVal) {
+            if (empty($filterVal)) {
+                continue;
+            }
+            $instance->filters[$filterKey] = $filterVal === 'true' ? '' : $filterVal;
+        }
+        if (isset($inputs['types']) && count($inputs['types']) < 4) {
+            $instance->filters['type'] = implode('|', $inputs['types']);
+        }
+        return $instance;
+    }
+
+    /**
+     * Decode a search string into an array of terms.
+     */
+    protected static function decode(string $searchString): array
+    {
+        $terms = [
+            'searches' => [],
+            'exacts' => [],
+            'tags' => [],
+            'filters' => []
+        ];
+
+        $patterns = [
+            'exacts' => '/"(.*?)"/',
+            'tags' => '/\[(.*?)\]/',
+            'filters' => '/\{(.*?)\}/'
+        ];
+
+        // Parse special terms
+        foreach ($patterns as $termType => $pattern) {
+            $matches = [];
+            preg_match_all($pattern, $searchString, $matches);
+            if (count($matches) > 0) {
+                $terms[$termType] = $matches[1];
+                $searchString = preg_replace($pattern, '', $searchString);
+            }
+        }
+
+        // Parse standard terms
+        foreach (explode(' ', trim($searchString)) as $searchTerm) {
+            if ($searchTerm !== '') {
+                $terms['searches'][] = $searchTerm;
+            }
+        }
+
+        // Split filter values out
+        $splitFilters = [];
+        foreach ($terms['filters'] as $filter) {
+            $explodedFilter = explode(':', $filter, 2);
+            $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
+        }
+        $terms['filters'] = $splitFilters;
+
+        return $terms;
+    }
+
+    /**
+     * Encode this instance to a search string.
+     */
+    public function toString(): string
+    {
+        $string = implode(' ', $this->searches ?? []);
+
+        foreach ($this->exacts as $term) {
+            $string .= ' "' . $term . '"';
+        }
+
+        foreach ($this->tags as $term) {
+            $string .= " [{$term}]";
+        }
+
+        foreach ($this->filters as $filterName => $filterVal) {
+            $string .= ' {' . $filterName . ($filterVal ? ':' . $filterVal : '') . '}';
+        }
+
+        return $string;
+    }
+
+}
\ No newline at end of file
index ee9b87786a57a1e059ed050621f0c694427b17cb..11b731cd0153591e2cfd7b6b71f88504f088cd92 100644 (file)
@@ -39,10 +39,6 @@ class SearchService
 
     /**
      * SearchService constructor.
-     * @param SearchTerm $searchTerm
-     * @param EntityProvider $entityProvider
-     * @param Connection $db
-     * @param PermissionService $permissionService
      */
     public function __construct(SearchTerm $searchTerm, EntityProvider $entityProvider, Connection $db, PermissionService $permissionService)
     {
@@ -54,7 +50,6 @@ class SearchService
 
     /**
      * Set the database connection
-     * @param Connection $connection
      */
     public function setConnection(Connection $connection)
     {
@@ -63,23 +58,18 @@ class SearchService
 
     /**
      * Search all entities in the system.
-     * @param string $searchString
-     * @param string $entityType
-     * @param int $page
-     * @param int $count - Count of each entity to search, Total returned could can be larger and not guaranteed.
-     * @param string $action
-     * @return array[int, Collection];
+     * The provided count is for each entity to search,
+     * Total returned could can be larger and not guaranteed.
      */
-    public function searchEntities($searchString, $entityType = 'all', $page = 1, $count = 20, $action = 'view')
+    public function searchEntities(SearchOptions $searchOpts, string $entityType = 'all', int $page = 1, int $count = 20, string $action = 'view'): array
     {
-        $terms = $this->parseSearchString($searchString);
         $entityTypes = array_keys($this->entityProvider->all());
         $entityTypesToSearch = $entityTypes;
 
         if ($entityType !== 'all') {
             $entityTypesToSearch = $entityType;
-        } else if (isset($terms['filters']['type'])) {
-            $entityTypesToSearch = explode('|', $terms['filters']['type']);
+        } else if (isset($searchOpts->filters['type'])) {
+            $entityTypesToSearch = explode('|', $searchOpts->filters['type']);
         }
 
         $results = collect();
@@ -90,8 +80,8 @@ class SearchService
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
-            $search = $this->searchEntityTable($terms, $entityType, $page, $count, $action);
-            $entityTotal = $this->searchEntityTable($terms, $entityType, $page, $count, $action, true);
+            $search = $this->searchEntityTable($searchOpts, $entityType, $page, $count, $action);
+            $entityTotal = $this->searchEntityTable($searchOpts, $entityType, $page, $count, $action, true);
             if ($entityTotal > $page * $count) {
                 $hasMore = true;
             }
@@ -103,29 +93,26 @@ class SearchService
             'total' => $total,
             'count' => count($results),
             'has_more' => $hasMore,
-            'results' => $results->sortByDesc('score')->values()
+            'results' => $results->sortByDesc('score')->values(),
         ];
     }
 
 
     /**
      * Search a book for entities
-     * @param integer $bookId
-     * @param string $searchString
-     * @return Collection
      */
-    public function searchBook($bookId, $searchString)
+    public function searchBook(int $bookId, string $searchString): Collection
     {
-        $terms = $this->parseSearchString($searchString);
+        $opts = SearchOptions::fromString($searchString);
         $entityTypes = ['page', 'chapter'];
-        $entityTypesToSearch = isset($terms['filters']['type']) ? explode('|', $terms['filters']['type']) : $entityTypes;
+        $entityTypesToSearch = isset($opts->filters['type']) ? explode('|', $opts->filters['type']) : $entityTypes;
 
         $results = collect();
         foreach ($entityTypesToSearch as $entityType) {
             if (!in_array($entityType, $entityTypes)) {
                 continue;
             }
-            $search = $this->buildEntitySearchQuery($terms, $entityType)->where('book_id', '=', $bookId)->take(20)->get();
+            $search = $this->buildEntitySearchQuery($opts, $entityType)->where('book_id', '=', $bookId)->take(20)->get();
             $results = $results->merge($search);
         }
         return $results->sortByDesc('score')->take(20);
@@ -133,30 +120,23 @@ class SearchService
 
     /**
      * Search a book for entities
-     * @param integer $chapterId
-     * @param string $searchString
-     * @return Collection
      */
-    public function searchChapter($chapterId, $searchString)
+    public function searchChapter(int $chapterId, string $searchString): Collection
     {
-        $terms = $this->parseSearchString($searchString);
-        $pages = $this->buildEntitySearchQuery($terms, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get();
+        $opts = SearchOptions::fromString($searchString);
+        $pages = $this->buildEntitySearchQuery($opts, 'page')->where('chapter_id', '=', $chapterId)->take(20)->get();
         return $pages->sortByDesc('score');
     }
 
     /**
      * Search across a particular entity type.
-     * @param array $terms
-     * @param string $entityType
-     * @param int $page
-     * @param int $count
-     * @param string $action
-     * @param bool $getCount Return the total count of the search
+     * Setting getCount = true will return the total
+     * matching instead of the items themselves.
      * @return \Illuminate\Database\Eloquent\Collection|int|static[]
      */
-    public function searchEntityTable($terms, $entityType = 'page', $page = 1, $count = 20, $action = 'view', $getCount = false)
+    public function searchEntityTable(SearchOptions $searchOpts, string $entityType = 'page', int $page = 1, int $count = 20, string $action = 'view', bool $getCount = false)
     {
-        $query = $this->buildEntitySearchQuery($terms, $entityType, $action);
+        $query = $this->buildEntitySearchQuery($searchOpts, $entityType, $action);
         if ($getCount) {
             return $query->count();
         }
@@ -167,22 +147,18 @@ class SearchService
 
     /**
      * Create a search query for an entity
-     * @param array $terms
-     * @param string $entityType
-     * @param string $action
-     * @return EloquentBuilder
      */
-    protected function buildEntitySearchQuery($terms, $entityType = 'page', $action = 'view')
+    protected function buildEntitySearchQuery(SearchOptions $searchOpts, string $entityType = 'page', string $action = 'view'): EloquentBuilder
     {
         $entity = $this->entityProvider->get($entityType);
         $entitySelect = $entity->newQuery();
 
         // Handle normal search terms
-        if (count($terms['search']) > 0) {
+        if (count($searchOpts->searches) > 0) {
             $subQuery = $this->db->table('search_terms')->select('entity_id', 'entity_type', \DB::raw('SUM(score) as score'));
             $subQuery->where('entity_type', '=', $entity->getMorphClass());
-            $subQuery->where(function (Builder $query) use ($terms) {
-                foreach ($terms['search'] as $inputTerm) {
+            $subQuery->where(function (Builder $query) use ($searchOpts) {
+                foreach ($searchOpts->searches as $inputTerm) {
                     $query->orWhere('term', 'like', $inputTerm .'%');
                 }
             })->groupBy('entity_type', 'entity_id');
@@ -193,9 +169,9 @@ class SearchService
         }
 
         // Handle exact term matching
-        if (count($terms['exact']) > 0) {
-            $entitySelect->where(function (EloquentBuilder $query) use ($terms, $entity) {
-                foreach ($terms['exact'] as $inputTerm) {
+        if (count($searchOpts->exacts) > 0) {
+            $entitySelect->where(function (EloquentBuilder $query) use ($searchOpts, $entity) {
+                foreach ($searchOpts->exacts as $inputTerm) {
                     $query->where(function (EloquentBuilder $query) use ($inputTerm, $entity) {
                         $query->where('name', 'like', '%'.$inputTerm .'%')
                             ->orWhere($entity->textField, 'like', '%'.$inputTerm .'%');
@@ -205,12 +181,12 @@ class SearchService
         }
 
         // Handle tag searches
-        foreach ($terms['tags'] as $inputTerm) {
+        foreach ($searchOpts->tags as $inputTerm) {
             $this->applyTagSearch($entitySelect, $inputTerm);
         }
 
         // Handle filters
-        foreach ($terms['filters'] as $filterTerm => $filterValue) {
+        foreach ($searchOpts->filters as $filterTerm => $filterValue) {
             $functionName = Str::camel('filter_' . $filterTerm);
             if (method_exists($this, $functionName)) {
                 $this->$functionName($entitySelect, $entity, $filterValue);
@@ -220,60 +196,10 @@ class SearchService
         return $this->permissionService->enforceEntityRestrictions($entityType, $entitySelect, $action);
     }
 
-
-    /**
-     * Parse a search string into components.
-     * @param $searchString
-     * @return array
-     */
-    protected function parseSearchString($searchString)
-    {
-        $terms = [
-            'search' => [],
-            'exact' => [],
-            'tags' => [],
-            'filters' => []
-        ];
-
-        $patterns = [
-            'exact' => '/"(.*?)"/',
-            'tags' => '/\[(.*?)\]/',
-            'filters' => '/\{(.*?)\}/'
-        ];
-
-        // Parse special terms
-        foreach ($patterns as $termType => $pattern) {
-            $matches = [];
-            preg_match_all($pattern, $searchString, $matches);
-            if (count($matches) > 0) {
-                $terms[$termType] = $matches[1];
-                $searchString = preg_replace($pattern, '', $searchString);
-            }
-        }
-
-        // Parse standard terms
-        foreach (explode(' ', trim($searchString)) as $searchTerm) {
-            if ($searchTerm !== '') {
-                $terms['search'][] = $searchTerm;
-            }
-        }
-
-        // Split filter values out
-        $splitFilters = [];
-        foreach ($terms['filters'] as $filter) {
-            $explodedFilter = explode(':', $filter, 2);
-            $splitFilters[$explodedFilter[0]] = (count($explodedFilter) > 1) ? $explodedFilter[1] : '';
-        }
-        $terms['filters'] = $splitFilters;
-
-        return $terms;
-    }
-
     /**
      * Get the available query operators as a regex escaped list.
-     * @return mixed
      */
-    protected function getRegexEscapedOperators()
+    protected function getRegexEscapedOperators(): string
     {
         $escapedOperators = [];
         foreach ($this->queryOperators as $operator) {
@@ -284,11 +210,8 @@ class SearchService
 
     /**
      * Apply a tag search term onto a entity query.
-     * @param EloquentBuilder $query
-     * @param string $tagTerm
-     * @return mixed
      */
-    protected function applyTagSearch(EloquentBuilder $query, $tagTerm)
+    protected function applyTagSearch(EloquentBuilder $query, string $tagTerm): EloquentBuilder
     {
         preg_match("/^(.*?)((".$this->getRegexEscapedOperators().")(.*?))?$/", $tagTerm, $tagSplit);
         $query->whereHas('tags', function (EloquentBuilder $query) use ($tagSplit) {
@@ -318,7 +241,6 @@ class SearchService
 
     /**
      * Index the given entity.
-     * @param Entity $entity
      */
     public function indexEntity(Entity $entity)
     {
index 459a5264a4228148426f7a1174f96da09a32847e..e8bc556abefa102153b64953a6dc76c89784e206 100644 (file)
@@ -1,5 +1,7 @@
 <?php namespace BookStack\Entities;
 
+use Illuminate\Support\Str;
+
 class SlugGenerator
 {
 
@@ -32,9 +34,7 @@ class SlugGenerator
      */
     protected function formatNameAsSlug(string $name): string
     {
-        $slug = preg_replace('/[\+\/\\\?\@\}\{\.\,\=\[\]\#\&\!\*\'\;\:\$\%]/', '', mb_strtolower($name));
-        $slug = preg_replace('/\s{2,}/', ' ', $slug);
-        $slug = str_replace(' ', '-', $slug);
+        $slug = Str::slug($name);
         if ($slug === "") {
             $slug = substr(md5(rand(1, 500)), 0, 5);
         }
index a3bc1e8b9e222e102fe7a8c93b1d2161362a0e8b..57078522b748bbbf905b1836705a4d24c0695bbb 100644 (file)
@@ -9,7 +9,6 @@ use Illuminate\Database\Eloquent\ModelNotFoundException;
 use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
 use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
-use Illuminate\Http\Response;
 use Illuminate\Validation\ValidationException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -26,6 +25,7 @@ class Handler extends ExceptionHandler
         HttpException::class,
         ModelNotFoundException::class,
         ValidationException::class,
+        NotFoundException::class,
     ];
 
     /**
similarity index 96%
rename from app/Http/Controllers/Api/BooksApiController.php
rename to app/Http/Controllers/Api/BookApiController.php
index ac4ea171c7e9633606011f907b2078b52ffe3d86..8333eba3a1d3779431dbbffa39e4e9abd49837d2 100644 (file)
@@ -8,7 +8,7 @@ use Illuminate\Contracts\Container\BindingResolutionException;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 
-class BooksApiController extends ApiController
+class BookApiController extends ApiController
 {
 
     protected $bookRepo;
@@ -17,10 +17,12 @@ class BooksApiController extends ApiController
         'create' => [
             'name' => 'required|string|max:255',
             'description' => 'string|max:1000',
+            'tags' => 'array',
         ],
         'update' => [
             'name' => 'string|min:1|max:255',
             'description' => 'string|max:1000',
+            'tags' => 'array',
         ],
     ];
 
similarity index 96%
rename from app/Http/Controllers/Api/BooksExportApiController.php
rename to app/Http/Controllers/Api/BookExportApiController.php
index 605f8f4089aaa0cd9e40bd47fc27713c329414f9..31fe5250fd7e3f14d7d60b8ff51a5d4bc4c241fd 100644 (file)
@@ -5,9 +5,8 @@ use BookStack\Entities\ExportService;
 use BookStack\Entities\Repos\BookRepo;
 use Throwable;
 
-class BooksExportApiController extends ApiController
+class BookExportApiController extends ApiController
 {
-
     protected $bookRepo;
     protected $exportService;
 
diff --git a/app/Http/Controllers/Api/ChapterApiController.php b/app/Http/Controllers/Api/ChapterApiController.php
new file mode 100644 (file)
index 0000000..50aa883
--- /dev/null
@@ -0,0 +1,104 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Book;
+use BookStack\Entities\Chapter;
+use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Facades\Activity;
+use Illuminate\Database\Eloquent\Relations\HasMany;
+use Illuminate\Http\Request;
+
+class ChapterApiController extends ApiController
+{
+    protected $chapterRepo;
+
+    protected $rules = [
+        'create' => [
+            'book_id' => 'required|integer',
+            'name' => 'required|string|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+        'update' => [
+            'book_id' => 'integer',
+            'name' => 'string|min:1|max:255',
+            'description' => 'string|max:1000',
+            'tags' => 'array',
+        ],
+    ];
+
+    /**
+     * ChapterController constructor.
+     */
+    public function __construct(ChapterRepo $chapterRepo)
+    {
+        $this->chapterRepo = $chapterRepo;
+    }
+
+    /**
+     * Get a listing of chapters visible to the user.
+     */
+    public function list()
+    {
+        $chapters = Chapter::visible();
+        return $this->apiListingResponse($chapters, [
+            'id', 'book_id', 'name', 'slug', 'description', 'priority',
+            'created_at', 'updated_at', 'created_by', 'updated_by',
+        ]);
+    }
+
+    /**
+     * Create a new chapter in the system.
+     */
+    public function create(Request $request)
+    {
+        $this->validate($request, $this->rules['create']);
+
+        $bookId = $request->get('book_id');
+        $book = Book::visible()->findOrFail($bookId);
+        $this->checkOwnablePermission('chapter-create', $book);
+
+        $chapter = $this->chapterRepo->create($request->all(), $book);
+        Activity::add($chapter, 'chapter_create', $book->id);
+
+        return response()->json($chapter->load(['tags']));
+    }
+
+    /**
+     * View the details of a single chapter.
+     */
+    public function read(string $id)
+    {
+        $chapter = Chapter::visible()->with(['tags', 'createdBy', 'updatedBy', 'pages' => function (HasMany $query) {
+            $query->visible()->get(['id', 'name', 'slug']);
+        }])->findOrFail($id);
+        return response()->json($chapter);
+    }
+
+    /**
+     * Update the details of a single chapter.
+     */
+    public function update(Request $request, string $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $this->checkOwnablePermission('chapter-update', $chapter);
+
+        $updatedChapter = $this->chapterRepo->update($chapter, $request->all());
+        Activity::add($chapter, 'chapter_update', $chapter->book->id);
+
+        return response()->json($updatedChapter->load(['tags']));
+    }
+
+    /**
+     * Delete a chapter from the system.
+     */
+    public function delete(string $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $this->checkOwnablePermission('chapter-delete', $chapter);
+
+        $this->chapterRepo->destroy($chapter);
+        Activity::addMessage('chapter_delete', $chapter->name, $chapter->book->id);
+
+        return response('', 204);
+    }
+}
diff --git a/app/Http/Controllers/Api/ChapterExportApiController.php b/app/Http/Controllers/Api/ChapterExportApiController.php
new file mode 100644 (file)
index 0000000..f19f29e
--- /dev/null
@@ -0,0 +1,54 @@
+<?php namespace BookStack\Http\Controllers\Api;
+
+use BookStack\Entities\Chapter;
+use BookStack\Entities\ExportService;
+use BookStack\Entities\Repos\BookRepo;
+use Throwable;
+
+class ChapterExportApiController extends ApiController
+{
+    protected $chapterRepo;
+    protected $exportService;
+
+    /**
+     * ChapterExportController constructor.
+     */
+    public function __construct(BookRepo $chapterRepo, ExportService $exportService)
+    {
+        $this->chapterRepo = $chapterRepo;
+        $this->exportService = $exportService;
+        parent::__construct();
+    }
+
+    /**
+     * Export a chapter as a PDF file.
+     * @throws Throwable
+     */
+    public function exportPdf(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $pdfContent = $this->exportService->chapterToPdf($chapter);
+        return $this->downloadResponse($pdfContent, $chapter->slug . '.pdf');
+    }
+
+    /**
+     * Export a chapter as a contained HTML file.
+     * @throws Throwable
+     */
+    public function exportHtml(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $htmlContent = $this->exportService->chapterToContainedHtml($chapter);
+        return $this->downloadResponse($htmlContent, $chapter->slug . '.html');
+    }
+
+    /**
+     * Export a chapter as a plain text file.
+     */
+    public function exportPlainText(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $textContent = $this->exportService->chapterToPlainText($chapter);
+        return $this->downloadResponse($textContent, $chapter->slug . '.txt');
+    }
+}
index 8f5da49ed83c10c5979b9e4b8eaa74ac324e14b9..0830693bc6a73769a51a26f3cda8239d163eac8e 100644 (file)
@@ -8,6 +8,7 @@ use BookStack\Uploads\AttachmentService;
 use Exception;
 use Illuminate\Contracts\Filesystem\FileNotFoundException;
 use Illuminate\Http\Request;
+use Illuminate\Support\MessageBag;
 use Illuminate\Validation\ValidationException;
 
 class AttachmentController extends Controller
@@ -60,25 +61,17 @@ class AttachmentController extends Controller
     /**
      * Update an uploaded attachment.
      * @throws ValidationException
-     * @throws NotFoundException
      */
     public function uploadUpdate(Request $request, $attachmentId)
     {
         $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
             'file' => 'required|file'
         ]);
 
-        $pageId = $request->get('uploaded_to');
-        $page = $this->pageRepo->getById($pageId);
-        $attachment = $this->attachment->findOrFail($attachmentId);
-
-        $this->checkOwnablePermission('page-update', $page);
+        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+        $this->checkOwnablePermission('view', $attachment->page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
-        
-        if (intval($pageId) !== intval($attachment->uploaded_to)) {
-            return $this->jsonError(trans('errors.attachment_page_mismatch'));
-        }
 
         $uploadedFile = $request->file('file');
 
@@ -92,57 +85,87 @@ class AttachmentController extends Controller
     }
 
     /**
-     * Update the details of an existing file.
-     * @throws ValidationException
-     * @throws NotFoundException
+     * Get the update form for an attachment.
+     * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function update(Request $request, $attachmentId)
+    public function getUpdateForm(string $attachmentId)
     {
-        $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
-            'name' => 'required|string|min:1|max:255',
-            'link' =>  'string|min:1|max:255'
-        ]);
-
-        $pageId = $request->get('uploaded_to');
-        $page = $this->pageRepo->getById($pageId);
         $attachment = $this->attachment->findOrFail($attachmentId);
 
-        $this->checkOwnablePermission('page-update', $page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
 
-        if (intval($pageId) !== intval($attachment->uploaded_to)) {
-            return $this->jsonError(trans('errors.attachment_page_mismatch'));
+        return view('attachments.manager-edit-form', [
+            'attachment' => $attachment,
+        ]);
+    }
+
+    /**
+     * Update the details of an existing file.
+     */
+    public function update(Request $request, string $attachmentId)
+    {
+        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+
+        try {
+            $this->validate($request, [
+                'attachment_edit_name' => 'required|string|min:1|max:255',
+                'attachment_edit_url' =>  'string|min:1|max:255'
+            ]);
+        } catch (ValidationException $exception) {
+            return response()->view('attachments.manager-edit-form', array_merge($request->only(['attachment_edit_name', 'attachment_edit_url']), [
+                'attachment' => $attachment,
+                'errors' => new MessageBag($exception->errors()),
+            ]), 422);
         }
 
-        $attachment = $this->attachmentService->updateFile($attachment, $request->all());
-        return response()->json($attachment);
+        $this->checkOwnablePermission('view', $attachment->page);
+        $this->checkOwnablePermission('page-update', $attachment->page);
+        $this->checkOwnablePermission('attachment-create', $attachment);
+
+        $attachment = $this->attachmentService->updateFile($attachment, [
+            'name' => $request->get('attachment_edit_name'),
+            'link' => $request->get('attachment_edit_url'),
+        ]);
+
+        return view('attachments.manager-edit-form', [
+            'attachment' => $attachment,
+        ]);
     }
 
     /**
      * Attach a link to a page.
-     * @throws ValidationException
      * @throws NotFoundException
      */
     public function attachLink(Request $request)
     {
-        $this->validate($request, [
-            'uploaded_to' => 'required|integer|exists:pages,id',
-            'name' => 'required|string|min:1|max:255',
-            'link' =>  'required|string|min:1|max:255'
-        ]);
+        $pageId = $request->get('attachment_link_uploaded_to');
+
+        try {
+            $this->validate($request, [
+                'attachment_link_uploaded_to' => 'required|integer|exists:pages,id',
+                'attachment_link_name' => 'required|string|min:1|max:255',
+                'attachment_link_url' =>  'required|string|min:1|max:255'
+            ]);
+        } catch (ValidationException $exception) {
+            return response()->view('attachments.manager-link-form', array_merge($request->only(['attachment_link_name', 'attachment_link_url']), [
+                'pageId' => $pageId,
+                'errors' => new MessageBag($exception->errors()),
+            ]), 422);
+        }
 
-        $pageId = $request->get('uploaded_to');
         $page = $this->pageRepo->getById($pageId);
 
         $this->checkPermission('attachment-create-all');
         $this->checkOwnablePermission('page-update', $page);
 
-        $attachmentName = $request->get('name');
-        $link = $request->get('link');
+        $attachmentName = $request->get('attachment_link_name');
+        $link = $request->get('attachment_link_url');
         $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, $pageId);
 
-        return response()->json($attachment);
+        return view('attachments.manager-link-form', [
+            'pageId' => $pageId,
+        ]);
     }
 
     /**
@@ -152,7 +175,9 @@ class AttachmentController extends Controller
     {
         $page = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-view', $page);
-        return response()->json($page->attachments);
+        return view('attachments.manager-list', [
+            'attachments' => $page->attachments->all(),
+        ]);
     }
 
     /**
@@ -163,14 +188,13 @@ class AttachmentController extends Controller
     public function sortForPage(Request $request, int $pageId)
     {
         $this->validate($request, [
-            'files' => 'required|array',
-            'files.*.id' => 'required|integer',
+            'order' => 'required|array',
         ]);
         $page = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-update', $page);
 
-        $attachments = $request->get('files');
-        $this->attachmentService->updateFileOrderWithinPage($attachments, $pageId);
+        $attachmentOrder = $request->get('order');
+        $this->attachmentService->updateFileOrderWithinPage($attachmentOrder, $pageId);
         return response()->json(['message' => trans('entities.attachments_order_updated')]);
     }
 
@@ -179,7 +203,7 @@ class AttachmentController extends Controller
      * @throws FileNotFoundException
      * @throws NotFoundException
      */
-    public function get(int $attachmentId)
+    public function get(string $attachmentId)
     {
         $attachment = $this->attachment->findOrFail($attachmentId);
         try {
@@ -200,11 +224,9 @@ class AttachmentController extends Controller
 
     /**
      * Delete a specific attachment in the system.
-     * @param $attachmentId
-     * @return mixed
      * @throws Exception
      */
-    public function delete(int $attachmentId)
+    public function delete(string $attachmentId)
     {
         $attachment = $this->attachment->findOrFail($attachmentId);
         $this->checkOwnablePermission('attachment-delete', $attachment);
diff --git a/app/Http/Controllers/AuditLogController.php b/app/Http/Controllers/AuditLogController.php
new file mode 100644 (file)
index 0000000..a3ef01b
--- /dev/null
@@ -0,0 +1,51 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Actions\Activity;
+use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
+
+class AuditLogController extends Controller
+{
+
+    public function index(Request $request)
+    {
+        $this->checkPermission('settings-manage');
+        $this->checkPermission('users-manage');
+
+        $listDetails = [
+            'order' => $request->get('order', 'desc'),
+            'event' => $request->get('event', ''),
+            'sort' => $request->get('sort', 'created_at'),
+            'date_from' => $request->get('date_from', ''),
+            'date_to' => $request->get('date_to', ''),
+        ];
+
+        $query = Activity::query()
+            ->with(['entity', 'user'])
+            ->orderBy($listDetails['sort'], $listDetails['order']);
+
+        if ($listDetails['event']) {
+            $query->where('key', '=', $listDetails['event']);
+        }
+
+        if ($listDetails['date_from']) {
+            $query->where('created_at', '>=', $listDetails['date_from']);
+        }
+        if ($listDetails['date_to']) {
+            $query->where('created_at', '<=', $listDetails['date_to']);
+        }
+
+        $activities = $query->paginate(100);
+        $activities->appends($listDetails);
+
+        $keys = DB::table('activities')->select('key')->distinct()->pluck('key');
+        $this->setPageTitle(trans('settings.audit'));
+        return view('settings.audit', [
+            'activities' => $activities,
+            'listDetails' => $listDetails,
+            'activityKeys' => $keys,
+        ]);
+    }
+}
index fb2573b5cc2f32abc3f3b70f7b8eeeb0c8d125ab..8084ce1a5dcfa220af09c73b21f711bdcc363dce 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace BookStack\Http\Controllers\Auth;
 
+use Activity;
 use BookStack\Auth\Access\SocialAuthService;
 use BookStack\Exceptions\LoginAttemptEmailNeededException;
 use BookStack\Exceptions\LoginAttemptException;
@@ -76,9 +77,13 @@ class LoginController extends Controller
             ]);
         }
 
+        // Store the previous location for redirect after login
         $previous = url()->previous('');
-        if (setting('app-public') && $previous && $previous !== url('/login')) {
-            redirect()->setIntendedUrl($previous);
+        if ($previous && $previous !== url('/login') && setting('app-public')) {
+            $isPreviousFromInstance = (strpos($previous, url('/')) === 0);
+            if ($isPreviousFromInstance) {
+                redirect()->setIntendedUrl($previous);
+            }
         }
 
         return view('auth.login', [
@@ -98,6 +103,7 @@ class LoginController extends Controller
     public function login(Request $request)
     {
         $this->validateLogin($request);
+        $username = $request->get($this->username());
 
         // If the class is using the ThrottlesLogins trait, we can automatically throttle
         // the login attempts for this application. We'll key this by the username and
@@ -106,6 +112,7 @@ class LoginController extends Controller
             $this->hasTooManyLoginAttempts($request)) {
             $this->fireLockoutEvent($request);
 
+            Activity::logFailedLogin($username);
             return $this->sendLockoutResponse($request);
         }
 
@@ -114,6 +121,7 @@ class LoginController extends Controller
                 return $this->sendLoginResponse($request);
             }
         } catch (LoginAttemptException $exception) {
+            Activity::logFailedLogin($username);
             return $this->sendLoginAttemptExceptionResponse($exception, $request);
         }
 
@@ -122,6 +130,7 @@ class LoginController extends Controller
         // user surpasses their maximum number of attempts they will get locked out.
         $this->incrementLoginAttempts($request);
 
+        Activity::logFailedLogin($username);
         return $this->sendFailedLoginResponse($request);
     }
 
index 2e8e8ed2ee12b005d58f9aab0492ef99dae0a56f..6a1dfcb0140062d0fcabcffe9174f226f1cacc48 100644 (file)
@@ -8,6 +8,7 @@ use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Exceptions\HttpResponseException;
 use Illuminate\Http\Request;
 use Illuminate\Routing\Controller as BaseController;
+use Illuminate\Validation\ValidationException;
 
 abstract class Controller extends BaseController
 {
@@ -132,23 +133,6 @@ abstract class Controller extends BaseController
         return response()->json(['message' => $messageText, 'status' => 'error'], $statusCode);
     }
 
-    /**
-     * Create the response for when a request fails validation.
-     * @param  \Illuminate\Http\Request  $request
-     * @param  array  $errors
-     * @return \Symfony\Component\HttpFoundation\Response
-     */
-    protected function buildFailedValidationResponse(Request $request, array $errors)
-    {
-        if ($request->expectsJson()) {
-            return response()->json(['validation' => $errors], 422);
-        }
-
-        return redirect()->to($this->getRedirectUrl())
-            ->withInput($request->input())
-            ->withErrors($errors, $this->errorBag());
-    }
-
     /**
      * Create a response that forces a download in the browser.
      * @param string $content
index 106dfd63089a6111f042bfc76a32fd4c6ebdde2f..29b1e9027ea128ff9189cc086e97a336c5381441 100644 (file)
@@ -30,7 +30,10 @@ class DrawioImageController extends Controller
         $parentTypeFilter = $request->get('filter_type', null);
 
         $imgData = $this->imageRepo->getEntityFiltered('drawio', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
-        return response()->json($imgData);
+        return view('components.image-manager-list', [
+            'images' => $imgData['images'],
+            'hasMore' => $imgData['has_more'],
+        ]);
     }
 
     /**
@@ -72,6 +75,7 @@ class DrawioImageController extends Controller
         if ($imageData === null) {
             return $this->jsonError("Image data could not be found");
         }
+
         return response()->json([
             'content' => base64_encode($imageData)
         ]);
index e506215ca43b919a9d79d4b6ba743f33c39e13ca..61907c0039bc7f72d8ec7cdd34804cf5f33536b1 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Exceptions\ImageUploadException;
 use BookStack\Uploads\ImageRepo;
 use Illuminate\Http\Request;
 use BookStack\Http\Controllers\Controller;
+use Illuminate\Validation\ValidationException;
 
 class GalleryImageController extends Controller
 {
@@ -13,7 +14,6 @@ class GalleryImageController extends Controller
 
     /**
      * GalleryImageController constructor.
-     * @param ImageRepo $imageRepo
      */
     public function __construct(ImageRepo $imageRepo)
     {
@@ -24,8 +24,6 @@ class GalleryImageController extends Controller
     /**
      * Get a list of gallery images, in a list.
      * Can be paged and filtered by entity.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function list(Request $request)
     {
@@ -35,14 +33,15 @@ class GalleryImageController extends Controller
         $parentTypeFilter = $request->get('filter_type', null);
 
         $imgData = $this->imageRepo->getEntityFiltered('gallery', $parentTypeFilter, $page, 24, $uploadedToFilter, $searchTerm);
-        return response()->json($imgData);
+        return view('components.image-manager-list', [
+            'images' => $imgData['images'],
+            'hasMore' => $imgData['has_more'],
+        ]);
     }
 
     /**
      * Store a new gallery image in the system.
-     * @param Request $request
-     * @return Illuminate\Http\JsonResponse
-     * @throws \Exception
+     * @throws ValidationException
      */
     public function create(Request $request)
     {
index 9c67704dd339bd0b21d3471b751206625386869b..7d06facffe14b963a53d645eb47d56d8a655b290 100644 (file)
@@ -6,8 +6,11 @@ use BookStack\Http\Controllers\Controller;
 use BookStack\Repos\PageRepo;
 use BookStack\Uploads\Image;
 use BookStack\Uploads\ImageRepo;
+use Exception;
 use Illuminate\Filesystem\Filesystem as File;
+use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
 
 class ImageController extends Controller
 {
@@ -17,9 +20,6 @@ class ImageController extends Controller
 
     /**
      * ImageController constructor.
-     * @param Image $image
-     * @param File $file
-     * @param ImageRepo $imageRepo
      */
     public function __construct(Image $image, File $file, ImageRepo $imageRepo)
     {
@@ -31,8 +31,6 @@ class ImageController extends Controller
 
     /**
      * Provide an image file from storage.
-     * @param string $path
-     * @return mixed
      */
     public function showImage(string $path)
     {
@@ -47,13 +45,10 @@ class ImageController extends Controller
 
     /**
      * Update image details
-     * @param Request $request
-     * @param integer $id
-     * @return \Illuminate\Http\JsonResponse
      * @throws ImageUploadException
-     * @throws \Exception
+     * @throws ValidationException
      */
-    public function update(Request $request, $id)
+    public function update(Request $request, string $id)
     {
         $this->validate($request, [
             'name' => 'required|min:2|string'
@@ -64,47 +59,50 @@ class ImageController extends Controller
         $this->checkOwnablePermission('image-update', $image);
 
         $image = $this->imageRepo->updateImageDetails($image, $request->all());
-        return response()->json($image);
+
+        $this->imageRepo->loadThumbs($image);
+        return view('components.image-manager-form', [
+            'image' => $image,
+            'dependantPages' => null,
+        ]);
     }
 
     /**
-     * Show the usage of an image on pages.
+     * Get the form for editing the given image.
+     * @throws Exception
      */
-    public function usage(int $id)
+    public function edit(Request $request, string $id)
     {
         $image = $this->imageRepo->getById($id);
         $this->checkImagePermission($image);
 
-        $pages = Page::visible()->where('html', 'like', '%' . $image->url . '%')->get(['id', 'name', 'slug', 'book_id']);
-        foreach ($pages as $page) {
-            $page->url = $page->getUrl();
-            $page->html = '';
-            $page->text = '';
+        if ($request->has('delete')) {
+            $dependantPages = $this->imageRepo->getPagesUsingImage($image);
         }
-        $result = count($pages) > 0 ? $pages : false;
 
-        return response()->json($result);
+        $this->imageRepo->loadThumbs($image);
+        return view('components.image-manager-form', [
+            'image' => $image,
+            'dependantPages' => $dependantPages ?? null,
+        ]);
     }
 
     /**
      * Deletes an image and all thumbnail/image files
-     * @param int $id
-     * @return \Illuminate\Http\JsonResponse
-     * @throws \Exception
+     * @throws Exception
      */
-    public function destroy($id)
+    public function destroy(string $id)
     {
         $image = $this->imageRepo->getById($id);
         $this->checkOwnablePermission('image-delete', $image);
         $this->checkImagePermission($image);
 
         $this->imageRepo->destroyImage($image);
-        return response()->json(trans('components.images_deleted'));
+        return response('');
     }
 
     /**
      * Check related page permission and ensure type is drawio or gallery.
-     * @param Image $image
      */
     protected function checkImagePermission(Image $image)
     {
diff --git a/app/Http/Controllers/MaintenanceController.php b/app/Http/Controllers/MaintenanceController.php
new file mode 100644 (file)
index 0000000..664a896
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Notifications\TestEmail;
+use BookStack\Uploads\ImageService;
+use Illuminate\Http\Request;
+
+class MaintenanceController extends Controller
+{
+    /**
+     * Show the page for application maintenance.
+     */
+    public function index()
+    {
+        $this->checkPermission('settings-manage');
+        $this->setPageTitle(trans('settings.maint'));
+
+        // Get application version
+        $version = trim(file_get_contents(base_path('version')));
+
+        return view('settings.maintenance', ['version' => $version]);
+    }
+
+    /**
+     * Action to clean-up images in the system.
+     */
+    public function cleanupImages(Request $request, ImageService $imageService)
+    {
+        $this->checkPermission('settings-manage');
+
+        $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
+        $dryRun = !($request->has('confirm'));
+
+        $imagesToDelete = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
+        $deleteCount = count($imagesToDelete);
+        if ($deleteCount === 0) {
+            $this->showWarningNotification(trans('settings.maint_image_cleanup_nothing_found'));
+            return redirect('/settings/maintenance')->withInput();
+        }
+
+        if ($dryRun) {
+            session()->flash('cleanup-images-warning', trans('settings.maint_image_cleanup_warning', ['count' => $deleteCount]));
+        } else {
+            $this->showSuccessNotification(trans('settings.maint_image_cleanup_success', ['count' => $deleteCount]));
+        }
+
+        return redirect('/settings/maintenance#image-cleanup')->withInput();
+    }
+
+    /**
+     * Action to send a test e-mail to the current user.
+     */
+    public function sendTestEmail()
+    {
+        $this->checkPermission('settings-manage');
+
+        try {
+            user()->notify(new TestEmail());
+            $this->showSuccessNotification(trans('settings.maint_send_test_email_success', ['address' => user()->email]));
+        } catch (\Exception $exception) {
+            $errorMessage = trans('errors.maintenance_test_email_failure') . "\n" . $exception->getMessage();
+            $this->showErrorNotification($errorMessage);
+        }
+
+        return redirect('/settings/maintenance#image-cleanup')->withInput();
+    }
+}
index b216c19a8e7689a2e4797f12c9da8e2c92512c0b..57d70fb3247f8177b4879e9f25eecf54dbbfd76d 100644 (file)
@@ -163,6 +163,8 @@ class PageController extends Controller
     public function getPageAjax(int $pageId)
     {
         $page = $this->pageRepo->getById($pageId);
+        $page->setHidden(array_diff($page->getHidden(), ['html', 'markdown']));
+        $page->addHidden(['book']);
         return response()->json($page);
     }
 
index 3c65b50ac5ae6e2d9c9769ba2f4a4ace572ea51f..797f5db8f43ff1ca9f8f561e029b888cec7a662d 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Entities\Managers\PageContent;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Facades\Activity;
@@ -46,6 +47,9 @@ class PageRevisionController extends Controller
         }
 
         $page->fill($revision->toArray());
+        // TODO - Refactor PageContent so we don't need to juggle this
+        $page->html = $revision->html;
+        $page->html = (new PageContent($page))->render();
 
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName' => $page->getShortName()]));
         return view('pages.revision', [
@@ -73,6 +77,9 @@ class PageRevisionController extends Controller
         $diff = (new Htmldiff)->diff($prevContent, $revision->html);
 
         $page->fill($revision->toArray());
+        // TODO - Refactor PageContent so we don't need to juggle this
+        $page->html = $revision->html;
+        $page->html = (new PageContent($page))->render();
         $this->setPageTitle(trans('entities.pages_revision_named', ['pageName'=>$page->getShortName()]));
 
         return view('pages.revision', [
index 148ae5cd65a3049e421ea2b331f3bfa34f31ed4d..1200d44ab69092ddcd3e498a2abf9ac961386f7a 100644 (file)
@@ -2,7 +2,9 @@
 
 use BookStack\Auth\Permissions\PermissionsRepo;
 use BookStack\Exceptions\PermissionsException;
+use Exception;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
 
 class PermissionController extends Controller
 {
@@ -11,7 +13,6 @@ class PermissionController extends Controller
 
     /**
      * PermissionController constructor.
-     * @param \BookStack\Auth\Permissions\PermissionsRepo $permissionsRepo
      */
     public function __construct(PermissionsRepo $permissionsRepo)
     {
@@ -31,7 +32,6 @@ class PermissionController extends Controller
 
     /**
      * Show the form to create a new role
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
     public function createRole()
     {
@@ -41,15 +41,13 @@ class PermissionController extends Controller
 
     /**
      * Store a new role in the system.
-     * @param Request $request
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
      */
     public function storeRole(Request $request)
     {
         $this->checkPermission('user-roles-manage');
         $this->validate($request, [
-            'display_name' => 'required|min:3|max:200',
-            'description' => 'max:250'
+            'display_name' => 'required|min:3|max:180',
+            'description' => 'max:180'
         ]);
 
         $this->permissionsRepo->saveNewRole($request->all());
@@ -59,11 +57,9 @@ class PermissionController extends Controller
 
     /**
      * Show the form for editing a user role.
-     * @param $id
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      * @throws PermissionsException
      */
-    public function editRole($id)
+    public function editRole(string $id)
     {
         $this->checkPermission('user-roles-manage');
         $role = $this->permissionsRepo->getRoleById($id);
@@ -75,18 +71,14 @@ class PermissionController extends Controller
 
     /**
      * Updates a user role.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
-     * @throws PermissionsException
-     * @throws \Illuminate\Validation\ValidationException
+     * @throws ValidationException
      */
-    public function updateRole(Request $request, $id)
+    public function updateRole(Request $request, string $id)
     {
         $this->checkPermission('user-roles-manage');
         $this->validate($request, [
-            'display_name' => 'required|min:3|max:200',
-            'description' => 'max:250'
+            'display_name' => 'required|min:3|max:180',
+            'description' => 'max:180'
         ]);
 
         $this->permissionsRepo->updateRole($id, $request->all());
@@ -97,10 +89,8 @@ class PermissionController extends Controller
     /**
      * Show the view to delete a role.
      * Offers the chance to migrate users.
-     * @param $id
-     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
      */
-    public function showDeleteRole($id)
+    public function showDeleteRole(string $id)
     {
         $this->checkPermission('user-roles-manage');
         $role = $this->permissionsRepo->getRoleById($id);
@@ -113,11 +103,9 @@ class PermissionController extends Controller
     /**
      * Delete a role from the system,
      * Migrate from a previous role if set.
-     * @param Request $request
-     * @param $id
-     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
+     * @throws Exception
      */
-    public function deleteRole(Request $request, $id)
+    public function deleteRole(Request $request, string $id)
     {
         $this->checkPermission('user-roles-manage');
 
index a5d57741d5295d9a315ba190c3dec2e9a57a3cc6..8105843b576acb9072651c878190a5489968f7b4 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Entities\Bookshelf;
 use BookStack\Entities\Entity;
 use BookStack\Entities\Managers\EntityContext;
 use BookStack\Entities\SearchService;
+use BookStack\Entities\SearchOptions;
 use Illuminate\Http\Request;
 
 class SearchController extends Controller
@@ -33,20 +34,22 @@ class SearchController extends Controller
      */
     public function search(Request $request)
     {
-        $searchTerm = $request->get('term');
-        $this->setPageTitle(trans('entities.search_for_term', ['term' => $searchTerm]));
+        $searchOpts = SearchOptions::fromRequest($request);
+        $fullSearchString = $searchOpts->toString();
+        $this->setPageTitle(trans('entities.search_for_term', ['term' => $fullSearchString]));
 
         $page = intval($request->get('page', '0')) ?: 1;
-        $nextPageLink = url('/search?term=' . urlencode($searchTerm) . '&page=' . ($page+1));
+        $nextPageLink = url('/search?term=' . urlencode($fullSearchString) . '&page=' . ($page+1));
 
-        $results = $this->searchService->searchEntities($searchTerm, 'all', $page, 20);
+        $results = $this->searchService->searchEntities($searchOpts, 'all', $page, 20);
 
         return view('search.all', [
             'entities'   => $results['results'],
             'totalResults' => $results['total'],
-            'searchTerm' => $searchTerm,
+            'searchTerm' => $fullSearchString,
             'hasNextPage' => $results['has_more'],
-            'nextPageLink' => $nextPageLink
+            'nextPageLink' => $nextPageLink,
+            'options' => $searchOpts,
         ]);
     }
 
@@ -84,7 +87,7 @@ class SearchController extends Controller
         // Search for entities otherwise show most popular
         if ($searchTerm !== false) {
             $searchTerm .= ' {type:'. implode('|', $entityTypes) .'}';
-            $entities = $this->searchService->searchEntities($searchTerm, 'all', 1, 20, $permission)['results'];
+            $entities = $this->searchService->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
         } else {
             $entities = $this->viewService->getPopular(20, 0, $entityTypes, $permission);
         }
index feb6521f3a69a68bedbe4573d1edfac098da01aa..50d91d3881e39362908bfe30aaa3a8168042355b 100644 (file)
@@ -1,9 +1,7 @@
 <?php namespace BookStack\Http\Controllers;
 
 use BookStack\Auth\User;
-use BookStack\Notifications\TestEmail;
 use BookStack\Uploads\ImageRepo;
-use BookStack\Uploads\ImageService;
 use Illuminate\Http\Request;
 
 class SettingController extends Controller
@@ -74,63 +72,4 @@ class SettingController extends Controller
         $redirectLocation = '/settings#' . $request->get('section', '');
         return redirect(rtrim($redirectLocation, '#'));
     }
-
-    /**
-     * Show the page for application maintenance.
-     */
-    public function showMaintenance()
-    {
-        $this->checkPermission('settings-manage');
-        $this->setPageTitle(trans('settings.maint'));
-
-        // Get application version
-        $version = trim(file_get_contents(base_path('version')));
-
-        return view('settings.maintenance', ['version' => $version]);
-    }
-
-    /**
-     * Action to clean-up images in the system.
-     */
-    public function cleanupImages(Request $request, ImageService $imageService)
-    {
-        $this->checkPermission('settings-manage');
-
-        $checkRevisions = !($request->get('ignore_revisions', 'false') === 'true');
-        $dryRun = !($request->has('confirm'));
-
-        $imagesToDelete = $imageService->deleteUnusedImages($checkRevisions, $dryRun);
-        $deleteCount = count($imagesToDelete);
-        if ($deleteCount === 0) {
-            $this->showWarningNotification(trans('settings.maint_image_cleanup_nothing_found'));
-            return redirect('/settings/maintenance')->withInput();
-        }
-
-        if ($dryRun) {
-            session()->flash('cleanup-images-warning', trans('settings.maint_image_cleanup_warning', ['count' => $deleteCount]));
-        } else {
-            $this->showSuccessNotification(trans('settings.maint_image_cleanup_success', ['count' => $deleteCount]));
-        }
-
-        return redirect('/settings/maintenance#image-cleanup')->withInput();
-    }
-
-    /**
-     * Action to send a test e-mail to the current user.
-     */
-    public function sendTestEmail()
-    {
-        $this->checkPermission('settings-manage');
-
-        try {
-            user()->notify(new TestEmail());
-            $this->showSuccessNotification(trans('settings.maint_send_test_email_success', ['address' => user()->email]));
-        } catch (\Exception $exception) {
-            $errorMessage = trans('errors.maintenance_test_email_failure') . "\n" . $exception->getMessage();
-            $this->showErrorNotification($errorMessage);
-        }
-
-
-        return redirect('/settings/maintenance#image-cleanup')->withInput();
-    }
 }
index 6abbeeebaa451e437e8b150a2ae496f15fe37827..8c6d6748fa5b79d41090e56f8fd1b1c73dab57c4 100644 (file)
@@ -10,7 +10,6 @@ class TagController extends Controller
 
     /**
      * TagController constructor.
-     * @param $tagRepo
      */
     public function __construct(TagRepo $tagRepo)
     {
@@ -18,39 +17,23 @@ class TagController extends Controller
         parent::__construct();
     }
 
-    /**
-     * Get all the Tags for a particular entity
-     * @param $entityType
-     * @param $entityId
-     * @return \Illuminate\Http\JsonResponse
-     */
-    public function getForEntity($entityType, $entityId)
-    {
-        $tags = $this->tagRepo->getForEntity($entityType, $entityId);
-        return response()->json($tags);
-    }
-
     /**
      * Get tag name suggestions from a given search term.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function getNameSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', false);
+        $searchTerm = $request->get('search', null);
         $suggestions = $this->tagRepo->getNameSuggestions($searchTerm);
         return response()->json($suggestions);
     }
 
     /**
      * Get tag value suggestions from a given search term.
-     * @param Request $request
-     * @return \Illuminate\Http\JsonResponse
      */
     public function getValueSuggestions(Request $request)
     {
-        $searchTerm = $request->get('search', false);
-        $tagName = $request->get('name', false);
+        $searchTerm = $request->get('search', null);
+        $tagName = $request->get('name', null);
         $suggestions = $this->tagRepo->getValueSuggestions($searchTerm, $tagName);
         return response()->json($suggestions);
     }
index 97ec31dcce0b2dfa2a539319afe126f23eb79788..651dedc0d855d5f54ecc2aba6b2def4e2feced1c 100644 (file)
@@ -66,8 +66,8 @@ class UserController extends Controller
     {
         $this->checkPermission('users-manage');
         $validationRules = [
-            'name'             => 'required',
-            'email'            => 'required|email|unique:users,email'
+            'name'  => 'required',
+            'email' => 'required|email|unique:users,email'
         ];
 
         $authMethod = config('auth.method');
index 15962b3b00471d1fc55dd2a229824b50319886d6..728057bed175b42a880014b0ecfbd6c3962d2701 100644 (file)
@@ -35,9 +35,9 @@ class ApiAuthenticate
     {
         // Return if the user is already found to be signed in via session-based auth.
         // This is to make it easy to browser the API via browser after just logging into the system.
-        if (signedInUser()) {
+        if (signedInUser() || session()->isStarted()) {
             $this->ensureEmailConfirmedIfRequested();
-            if (!auth()->user()->can('access-api')) {
+            if (!user()->can('access-api')) {
                 throw new ApiAuthException(trans('errors.api_user_no_api_permission'), 403);
             }
             return;
index 9a8affa8842fbe7e0a944462f5ee24770d752a79..df8c44d351cc92784bc8adaec1f642ea0c1719a0 100644 (file)
@@ -44,6 +44,10 @@ class Authenticate
             ], 401);
         }
 
+        if (session()->get('sent-email-confirmation') === true) {
+            return redirect('/register/confirm');
+        }
+
         return redirect('/register/confirm/awaiting');
     }
 }
index d24e8a9b5b88c8f8cb0b08f60a05506942561021..c0ac7a7c4ec781ccfc1939493756c695e4f7ad6d 100644 (file)
@@ -19,6 +19,7 @@ class Localization
      */
     protected $localeMap = [
         'ar' => 'ar',
+        'bg' => 'bg_BG',
         'da' => 'da_DK',
         'de' => 'de_DE',
         'de_informal' => 'de_DE',
index 3f0b447df7388a573602396d052bce2e5c5bca7a..66c032be587dca8c0a41c606df510c6e74d14479 100644 (file)
@@ -3,6 +3,13 @@
 use BookStack\Entities\Page;
 use BookStack\Ownable;
 
+/**
+ * @property int id
+ * @property string name
+ * @property string path
+ * @property string extension
+ * @property bool external
+ */
 class Attachment extends Ownable
 {
     protected $fillable = ['name', 'order'];
@@ -30,13 +37,28 @@ class Attachment extends Ownable
 
     /**
      * Get the url of this file.
-     * @return string
      */
-    public function getUrl()
+    public function getUrl(): string
     {
         if ($this->external && strpos($this->path, 'http') !== 0) {
             return $this->path;
         }
         return url('/attachments/' . $this->id);
     }
+
+    /**
+     * Generate a HTML link to this attachment.
+     */
+    public function htmlLink(): string
+    {
+        return '<a target="_blank" href="'.e($this->getUrl()).'">'.e($this->name).'</a>';
+    }
+
+    /**
+     * Generate a markdown link to this attachment.
+     */
+    public function markdownLink(): string
+    {
+        return '['. $this->name .']('. $this->getUrl() .')';
+    }
 }
index ae4fb6e967160787e1c46dde0e442228386f9af9..02220771aaee631e1d7bd0643d865d9196932b3e 100644 (file)
@@ -109,14 +109,14 @@ class AttachmentService extends UploadService
     }
 
     /**
-     * Updates the file ordering for a listing of attached files.
-     * @param array $attachmentList
-     * @param $pageId
+     * Updates the ordering for a listing of attached files.
      */
-    public function updateFileOrderWithinPage($attachmentList, $pageId)
+    public function updateFileOrderWithinPage(array $attachmentOrder, string $pageId)
     {
-        foreach ($attachmentList as $index => $attachment) {
-            Attachment::where('uploaded_to', '=', $pageId)->where('id', '=', $attachment['id'])->update(['order' => $index]);
+        foreach ($attachmentOrder as $index => $attachmentId) {
+            Attachment::query()->where('uploaded_to', '=', $pageId)
+                ->where('id', '=', $attachmentId)
+                ->update(['order' => $index]);
         }
     }
 
index b7a21809f18ab7347e44945b8f0933815bf57b7e..a0855508594b7c336252bc4d9856c5eb01db20ec 100644 (file)
@@ -185,7 +185,7 @@ class ImageRepo
      * Load thumbnails onto an image object.
      * @throws Exception
      */
-    protected function loadThumbs(Image $image)
+    public function loadThumbs(Image $image)
     {
         $image->thumbs = [
             'gallery' => $this->getThumbnail($image, 150, 150, false),
@@ -219,4 +219,20 @@ class ImageRepo
             return null;
         }
     }
+
+    /**
+     * Get the user visible pages using the given image.
+     */
+    public function getPagesUsingImage(Image $image): array
+    {
+        $pages = Page::visible()
+            ->where('html', 'like', '%' . $image->url . '%')
+            ->get(['id', 'name', 'slug', 'book_id']);
+
+        foreach ($pages as $page) {
+            $page->url = $page->getUrl();
+        }
+
+        return $pages->all();
+    }
 }
index 756149fe7a1bacdc2f11a2f4ee3672ceb50c8676..89744386d60208d9a85bf0ed1562c79af8a963d0 100644 (file)
@@ -124,29 +124,24 @@ class ImageService extends UploadService
     }
 
     /**
-     * Saves a new image
-     * @param string $imageName
-     * @param string $imageData
-     * @param string $type
-     * @param int $uploadedTo
-     * @return Image
+     * Save a new image into storage.
      * @throws ImageUploadException
      */
-    private function saveNew($imageName, $imageData, $type, $uploadedTo = 0)
+    private function saveNew(string $imageName, string $imageData, string $type, int $uploadedTo = 0): Image
     {
         $storage = $this->getStorage($type);
         $secureUploads = setting('app-secure-images');
-        $imageName = str_replace(' ', '-', $imageName);
+        $fileName = $this->cleanImageFileName($imageName);
 
         $imagePath = '/uploads/images/' . $type . '/' . Date('Y-m') . '/';
 
-        while ($storage->exists($imagePath . $imageName)) {
-            $imageName = Str::random(3) . $imageName;
+        while ($storage->exists($imagePath . $fileName)) {
+            $fileName = Str::random(3) . $fileName;
         }
 
-        $fullPath = $imagePath . $imageName;
+        $fullPath = $imagePath . $fileName;
         if ($secureUploads) {
-            $fullPath = $imagePath . Str::random(16) . '-' . $imageName;
+            $fullPath = $imagePath . Str::random(16) . '-' . $fileName;
         }
 
         try {
@@ -175,6 +170,23 @@ class ImageService extends UploadService
         return $image;
     }
 
+    /**
+     * Clean up an image file name to be both URL and storage safe.
+     */
+    protected function cleanImageFileName(string $name): string
+    {
+        $name = str_replace(' ', '-', $name);
+        $nameParts = explode('.', $name);
+        $extension = array_pop($nameParts);
+        $name = implode('.', $nameParts);
+        $name = Str::slug($name);
+
+        if (strlen($name) === 0) {
+            $name = Str::random(10);
+        }
+
+        return  $name . '.' . $extension;
+    }
 
     /**
      * Checks if the image is a gif. Returns true if it is, else false.
@@ -223,6 +235,7 @@ class ImageService extends UploadService
         $storage->setVisibility($thumbFilePath, 'public');
         $this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 60 * 72);
 
+
         return $this->getPublicUrl($thumbFilePath);
     }
 
@@ -292,11 +305,9 @@ class ImageService extends UploadService
 
     /**
      * Destroys an image at the given path.
-     * Searches for image thumbnails in addition to main provided path..
-     * @param string $path
-     * @return bool
+     * Searches for image thumbnails in addition to main provided path.
      */
-    protected function destroyImagesFromPath(string $path)
+    protected function destroyImagesFromPath(string $path): bool
     {
         $storage = $this->getStorage();
 
@@ -306,8 +317,7 @@ class ImageService extends UploadService
 
         // Delete image files
         $imagesToDelete = $allImages->filter(function ($imagePath) use ($imageFileName) {
-            $expectedIndex = strlen($imagePath) - strlen($imageFileName);
-            return strpos($imagePath, $imageFileName) === $expectedIndex;
+            return basename($imagePath) === $imageFileName;
         });
         $storage->delete($imagesToDelete->all());
 
index 65da1853b54e54bb43a8cb8fe5d9959a3a877578..83017c37dddda3c81043c7f29b1aad080e8e346e 100644 (file)
@@ -153,10 +153,6 @@ function icon(string $name, array $attrs = []): string
  * Generate a url with multiple parameters for sorting purposes.
  * Works out the logic to set the correct sorting direction
  * Discards empty parameters and allows overriding.
- * @param string $path
- * @param array $data
- * @param array $overrideData
- * @return string
  */
 function sortUrl(string $path, array $data, array $overrideData = []): string
 {
@@ -166,7 +162,7 @@ function sortUrl(string $path, array $data, array $overrideData = []): string
     // Change sorting direction is already sorted on current attribute
     if (isset($overrideData['sort']) && $overrideData['sort'] === $data['sort']) {
         $queryData['order'] = ($data['order'] === 'asc') ? 'desc' : 'asc';
-    } else {
+    } elseif (isset($overrideData['sort'])) {
         $queryData['order'] = 'asc';
     }
 
index 80e8b0a619036d9a460452fb1fd62cadb0e99a45..59fc909d6b1e9f2298a510db246246c06bd46ebd 100644 (file)
         "ext-mbstring": "*",
         "ext-tidy": "*",
         "ext-xml": "*",
-        "barryvdh/laravel-dompdf": "^0.8.5",
-        "barryvdh/laravel-snappy": "^0.4.5",
+        "barryvdh/laravel-dompdf": "^0.8.6",
+        "barryvdh/laravel-snappy": "^0.4.7",
         "doctrine/dbal": "^2.9",
         "facade/ignition": "^1.4",
         "fideloper/proxy": "^4.0",
         "gathercontent/htmldiff": "^0.2.1",
         "intervention/image": "^2.5",
-        "laravel/framework": "^6.12",
+        "laravel/framework": "^6.18",
         "laravel/socialite": "^4.3.2",
         "league/commonmark": "^1.4",
         "league/flysystem-aws-s3-v3": "^1.0",
index 3ddd28e5a857542eb14c3cf77ad19e07db10eb85..b22874455b95bc9db5a0fd453ae38938dcaa6d8b 100644 (file)
@@ -4,20 +4,20 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "bbe47cff4f167fd6ce7047dff4602a78",
+    "content-hash": "34390536dd685e0bc49b179babaa06ec",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.134.3",
+            "version": "3.154.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "3de2711a47e7c3f5e93a5c83f019188fd23f852f"
+                "reference": "83a1382930359e4d4f4c9187239f059d5b282520"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3de2711a47e7c3f5e93a5c83f019188fd23f852f",
-                "reference": "3de2711a47e7c3f5e93a5c83f019188fd23f852f",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/83a1382930359e4d4f4c9187239f059d5b282520",
+                "reference": "83a1382930359e4d4f4c9187239f059d5b282520",
                 "shasum": ""
             },
             "require": {
@@ -40,6 +40,7 @@
                 "ext-pcntl": "*",
                 "ext-sockets": "*",
                 "nette/neon": "^2.3",
+                "paragonie/random_compat": ">= 2",
                 "phpunit/phpunit": "^4.8.35|^5.4.3",
                 "psr/cache": "^1.0",
                 "psr/simple-cache": "^1.0",
                 "s3",
                 "sdk"
             ],
-            "time": "2020-04-03T18:11:51+00:00"
+            "time": "2020-09-18T18:16:42+00:00"
         },
         {
             "name": "barryvdh/laravel-dompdf",
-            "version": "v0.8.6",
+            "version": "v0.8.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-dompdf.git",
-                "reference": "d7108f78cf5254a2d8c224542967f133e5a6d4e8"
+                "reference": "30310e0a675462bf2aa9d448c8dcbf57fbcc517d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/d7108f78cf5254a2d8c224542967f133e5a6d4e8",
-                "reference": "d7108f78cf5254a2d8c224542967f133e5a6d4e8",
+                "url": "https://api.github.com/repos/barryvdh/laravel-dompdf/zipball/30310e0a675462bf2aa9d448c8dcbf57fbcc517d",
+                "reference": "30310e0a675462bf2aa9d448c8dcbf57fbcc517d",
                 "shasum": ""
             },
             "require": {
                 "dompdf/dompdf": "^0.8",
-                "illuminate/support": "^5.5|^6|^7",
+                "illuminate/support": "^5.5|^6|^7|^8",
                 "php": ">=7"
             },
             "type": "library",
                 "laravel",
                 "pdf"
             ],
-            "time": "2020-02-25T20:44:34+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-07T11:50:18+00:00"
         },
         {
             "name": "barryvdh/laravel-snappy",
-            "version": "v0.4.7",
+            "version": "v0.4.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-snappy.git",
-                "reference": "c412d0c8f40b1326ba0fb16e94957fd1e68374f0"
+                "reference": "1903ab84171072b6bff8d98eb58d38b2c9aaf645"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/c412d0c8f40b1326ba0fb16e94957fd1e68374f0",
-                "reference": "c412d0c8f40b1326ba0fb16e94957fd1e68374f0",
+                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/1903ab84171072b6bff8d98eb58d38b2c9aaf645",
+                "reference": "1903ab84171072b6bff8d98eb58d38b2c9aaf645",
                 "shasum": ""
             },
             "require": {
-                "illuminate/filesystem": "^5.5|^6|^7",
-                "illuminate/support": "^5.5|^6|^7",
+                "illuminate/filesystem": "^5.5|^6|^7|^8",
+                "illuminate/support": "^5.5|^6|^7|^8",
                 "knplabs/knp-snappy": "^1",
                 "php": ">=7"
             },
                 "wkhtmltoimage",
                 "wkhtmltopdf"
             ],
-            "time": "2020-02-25T20:52:15+00:00"
+            "time": "2020-09-07T12:33:10+00:00"
         },
         {
             "name": "cogpowered/finediff",
         },
         {
             "name": "doctrine/cache",
-            "version": "1.10.0",
+            "version": "1.10.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62"
+                "reference": "13e3381b25847283a91948d04640543941309727"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/382e7f4db9a12dc6c19431743a2b096041bcdd62",
-                "reference": "382e7f4db9a12dc6c19431743a2b096041bcdd62",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
+                "reference": "13e3381b25847283a91948d04640543941309727",
                 "shasum": ""
             },
             "require": {
-                "php": "~7.1"
+                "php": "~7.1 || ^8.0"
             },
             "conflict": {
                 "doctrine/common": ">2.2,<2.4"
                 "redis",
                 "xcache"
             ],
-            "time": "2019-11-29T15:36:20+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fcache",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-07T18:54:01+00:00"
         },
         {
             "name": "doctrine/dbal",
-            "version": "v2.10.1",
+            "version": "2.10.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8"
+                "reference": "47433196b6390d14409a33885ee42b6208160643"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8",
-                "reference": "c2b8e6e82732a64ecde1cddf9e1e06cb8556e3d8",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/47433196b6390d14409a33885ee42b6208160643",
+                "reference": "47433196b6390d14409a33885ee42b6208160643",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^6.0",
+                "doctrine/coding-standard": "^8.1",
                 "jetbrains/phpstorm-stubs": "^2019.1",
-                "phpstan/phpstan": "^0.11.3",
-                "phpunit/phpunit": "^8.4.1",
-                "symfony/console": "^2.0.5|^3.0|^4.0|^5.0"
+                "nikic/php-parser": "^4.4",
+                "phpstan/phpstan": "^0.12.40",
+                "phpunit/phpunit": "^8.5.5",
+                "psalm/plugin-phpunit": "^0.10.0",
+                "symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
+                "vimeo/psalm": "^3.14.2"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
                 "sqlserver",
                 "sqlsrv"
             ],
-            "time": "2020-01-04T12:56:21+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fdbal",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-12T21:20:41+00:00"
         },
         {
             "name": "doctrine/event-manager",
-            "version": "1.1.0",
+            "version": "1.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/event-manager.git",
-                "reference": "629572819973f13486371cb611386eb17851e85c"
+                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/629572819973f13486371cb611386eb17851e85c",
-                "reference": "629572819973f13486371cb611386eb17851e85c",
+                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/41370af6a30faa9dc0368c4a6814d596e81aba7f",
+                "reference": "41370af6a30faa9dc0368c4a6814d596e81aba7f",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "conflict": {
                 "doctrine/common": "<2.9@dev"
                 "event system",
                 "events"
             ],
-            "time": "2019-11-10T09:48:07+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-29T18:28:51+00:00"
         },
         {
             "name": "doctrine/inflector",
-            "version": "1.3.1",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/inflector.git",
-                "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1"
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/inflector/zipball/ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
-                "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
+                "reference": "9cf661f4eb38f7c881cac67c75ea9b00bf97b210",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^6.2"
+                "doctrine/coding-standard": "^7.0",
+                "phpstan/phpstan": "^0.11",
+                "phpstan/phpstan-phpunit": "^0.11",
+                "phpstan/phpstan-strict-rules": "^0.11",
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3.x-dev"
+                    "dev-master": "2.0.x-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "Doctrine\\Common\\Inflector\\": "lib/Doctrine/Common/Inflector"
+                    "Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                     "email": "[email protected]"
                 }
             ],
-            "description": "Common String Manipulations with regard to casing and singular/plural rules.",
-            "homepage": "http://www.doctrine-project.org",
+            "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.",
+            "homepage": "https://www.doctrine-project.org/projects/inflector.html",
             "keywords": [
                 "inflection",
-                "pluralize",
-                "singularize",
-                "string"
+                "inflector",
+                "lowercase",
+                "manipulation",
+                "php",
+                "plural",
+                "singular",
+                "strings",
+                "uppercase",
+                "words"
+            ],
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector",
+                    "type": "tidelift"
+                }
             ],
-            "time": "2019-10-30T19:59:35+00:00"
+            "time": "2020-05-29T15:13:26+00:00"
         },
         {
             "name": "doctrine/lexer",
-            "version": "1.2.0",
+            "version": "1.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/lexer.git",
-                "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6"
+                "reference": "e864bbf5904cb8f5bb334f99209b48018522f042"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
-                "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/e864bbf5904cb8f5bb334f99209b48018522f042",
+                "reference": "e864bbf5904cb8f5bb334f99209b48018522f042",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2"
+                "php": "^7.2 || ^8.0"
             },
             "require-dev": {
                 "doctrine/coding-standard": "^6.0",
                 "parser",
                 "php"
             ],
-            "time": "2019-10-30T14:39:59+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-25T17:44:05+00:00"
         },
         {
             "name": "dompdf/dompdf",
-            "version": "v0.8.5",
+            "version": "v0.8.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/dompdf/dompdf.git",
-                "reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56"
+                "reference": "db91d81866c69a42dad1d2926f61515a1e3f42c5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/6782abfc090b132134cd6cea0ec6d76f0fce2c56",
-                "reference": "6782abfc090b132134cd6cea0ec6d76f0fce2c56",
+                "url": "https://api.github.com/repos/dompdf/dompdf/zipball/db91d81866c69a42dad1d2926f61515a1e3f42c5",
+                "reference": "db91d81866c69a42dad1d2926f61515a1e3f42c5",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-mbstring": "*",
-                "phenx/php-font-lib": "^0.5.1",
+                "phenx/php-font-lib": "^0.5.2",
                 "phenx/php-svg-lib": "^0.3.3",
                 "php": "^7.1"
             },
             "require-dev": {
+                "mockery/mockery": "^1.3",
                 "phpunit/phpunit": "^7.5",
                 "squizlabs/php_codesniffer": "^3.5"
             },
             "suggest": {
                 "ext-gd": "Needed to process images",
                 "ext-gmagick": "Improves image processing performance",
-                "ext-imagick": "Improves image processing performance"
+                "ext-imagick": "Improves image processing performance",
+                "ext-zlib": "Needed for pdf stream compression"
             },
             "type": "library",
             "extra": {
             ],
             "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter",
             "homepage": "https://github.com/dompdf/dompdf",
-            "time": "2020-02-20T03:52:51+00:00"
+            "time": "2020-08-30T22:54:22+00:00"
         },
         {
             "name": "dragonmantank/cron-expression",
         },
         {
             "name": "egulias/email-validator",
-            "version": "2.1.17",
+            "version": "2.1.20",
             "source": {
                 "type": "git",
                 "url": "https://github.com/egulias/EmailValidator.git",
-                "reference": "ade6887fd9bd74177769645ab5c474824f8a418a"
+                "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/ade6887fd9bd74177769645ab5c474824f8a418a",
-                "reference": "ade6887fd9bd74177769645ab5c474824f8a418a",
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/f46887bc48db66c7f38f668eb7d6ae54583617ff",
+                "reference": "f46887bc48db66c7f38f668eb7d6ae54583617ff",
                 "shasum": ""
             },
             "require": {
             },
             "autoload": {
                 "psr-4": {
-                    "Egulias\\EmailValidator\\": "EmailValidator"
+                    "Egulias\\EmailValidator\\": "src"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 "validation",
                 "validator"
             ],
-            "time": "2020-02-13T22:36:52+00:00"
+            "time": "2020-09-06T13:44:32+00:00"
         },
         {
             "name": "facade/flare-client-php",
-            "version": "1.3.2",
+            "version": "1.3.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/flare-client-php.git",
-                "reference": "db1e03426e7f9472c9ecd1092aff00f56aa6c004"
+                "reference": "451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/db1e03426e7f9472c9ecd1092aff00f56aa6c004",
-                "reference": "db1e03426e7f9472c9ecd1092aff00f56aa6c004",
+                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799",
+                "reference": "451fadf38e9f635e7f8e1f5b3cf5c9eb82f11799",
                 "shasum": ""
             },
             "require": {
                 "facade/ignition-contracts": "~1.0",
-                "illuminate/pipeline": "^5.5|^6.0|^7.0",
+                "illuminate/pipeline": "^5.5|^6.0|^7.0|^8.0",
                 "php": "^7.1",
                 "symfony/http-foundation": "^3.3|^4.1|^5.0",
+                "symfony/mime": "^3.4|^4.0|^5.1",
                 "symfony/var-dumper": "^3.4|^4.0|^5.0"
             },
             "require-dev": {
-                "larapack/dd": "^1.1",
+                "friendsofphp/php-cs-fixer": "^2.14",
                 "phpunit/phpunit": "^7.5.16",
                 "spatie/phpunit-snapshot-assertions": "^2.0"
             },
                 "flare",
                 "reporting"
             ],
-            "time": "2020-03-02T15:52:04+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/spatie",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-18T06:35:11+00:00"
         },
         {
             "name": "facade/ignition",
-            "version": "1.16.1",
+            "version": "1.16.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "af05ac5ee8587395d7474ec0681c08776a2cb09d"
+                "reference": "19674150bb46a4de0ba138c747f538fe7be11dbc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/af05ac5ee8587395d7474ec0681c08776a2cb09d",
-                "reference": "af05ac5ee8587395d7474ec0681c08776a2cb09d",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/19674150bb46a4de0ba138c747f538fe7be11dbc",
+                "reference": "19674150bb46a4de0ba138c747f538fe7be11dbc",
                 "shasum": ""
             },
             "require": {
                 "laravel",
                 "page"
             ],
-            "time": "2020-03-05T12:39:07+00:00"
+            "time": "2020-07-13T15:54:05+00:00"
         },
         {
             "name": "facade/ignition-contracts",
-            "version": "1.0.0",
+            "version": "1.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition-contracts.git",
-                "reference": "f445db0fb86f48e205787b2592840dd9c80ded28"
+                "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/f445db0fb86f48e205787b2592840dd9c80ded28",
-                "reference": "f445db0fb86f48e205787b2592840dd9c80ded28",
+                "url": "https://api.github.com/repos/facade/ignition-contracts/zipball/aeab1ce8b68b188a43e81758e750151ad7da796b",
+                "reference": "aeab1ce8b68b188a43e81758e750151ad7da796b",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.1"
             },
+            "require-dev": {
+                "friendsofphp/php-cs-fixer": "^2.14",
+                "phpunit/phpunit": "^7.5|^8.0",
+                "vimeo/psalm": "^3.12"
+            },
             "type": "library",
             "autoload": {
                 "psr-4": {
                 "flare",
                 "ignition"
             ],
-            "time": "2019-08-30T14:06:08+00:00"
+            "time": "2020-07-14T10:10:28+00:00"
         },
         {
             "name": "fideloper/proxy",
-            "version": "4.3.0",
+            "version": "4.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/fideloper/TrustedProxy.git",
-                "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a"
+                "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a",
-                "reference": "ec38ad69ee378a1eec04fb0e417a97cfaf7ed11a",
+                "url": "https://api.github.com/repos/fideloper/TrustedProxy/zipball/9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8",
+                "reference": "9beebf48a1c344ed67c1d36bb1b8709db7c3c1a8",
                 "shasum": ""
             },
             "require": {
                 "proxy",
                 "trusted proxy"
             ],
-            "time": "2020-02-22T01:51:47+00:00"
+            "time": "2020-06-23T01:36:47+00:00"
         },
         {
             "name": "filp/whoops",
-            "version": "2.7.1",
+            "version": "2.7.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/filp/whoops.git",
-                "reference": "fff6f1e4f36be0e0d0b84d66b413d9dcb0c49130"
+                "reference": "5d5fe9bb3d656b514d455645b3addc5f7ba7714d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/filp/whoops/zipball/fff6f1e4f36be0e0d0b84d66b413d9dcb0c49130",
-                "reference": "fff6f1e4f36be0e0d0b84d66b413d9dcb0c49130",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/5d5fe9bb3d656b514d455645b3addc5f7ba7714d",
+                "reference": "5d5fe9bb3d656b514d455645b3addc5f7ba7714d",
                 "shasum": ""
             },
             "require": {
                 "throwable",
                 "whoops"
             ],
-            "time": "2020-01-15T10:00:00+00:00"
+            "time": "2020-06-14T09:00:00+00:00"
         },
         {
             "name": "gathercontent/htmldiff",
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "6.5.2",
+            "version": "6.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "43ece0e75098b7ecd8d13918293029e555a50f82"
+                "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82",
-                "reference": "43ece0e75098b7ecd8d13918293029e555a50f82",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
+                "reference": "9d4290de1cfd701f38099ef7e183b64b4b7b0c5e",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^1.0",
                 "guzzlehttp/psr7": "^1.6.1",
-                "php": ">=5.5"
+                "php": ">=5.5",
+                "symfony/polyfill-intl-idn": "^1.17.0"
             },
             "require-dev": {
                 "ext-curl": "*",
                 "psr/log": "^1.1"
             },
             "suggest": {
-                "ext-intl": "Required for Internationalized Domain Name (IDN) support",
                 "psr/log": "Required for using the Log middleware"
             },
             "type": "library",
                 "rest",
                 "web service"
             ],
-            "time": "2019-12-23T11:57:10+00:00"
+            "time": "2020-06-16T21:01:06+00:00"
         },
         {
             "name": "guzzlehttp/promises",
         },
         {
             "name": "laravel/framework",
-            "version": "v6.18.3",
+            "version": "v6.18.40",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "4e48acfaba87f08320a2764d36c3b6a4a4112ccf"
+                "reference": "e42450df0896b7130ccdb5290a114424e18887c9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/4e48acfaba87f08320a2764d36c3b6a4a4112ccf",
-                "reference": "4e48acfaba87f08320a2764d36c3b6a4a4112ccf",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/e42450df0896b7130ccdb5290a114424e18887c9",
+                "reference": "e42450df0896b7130ccdb5290a114424e18887c9",
                 "shasum": ""
             },
             "require": {
-                "doctrine/inflector": "^1.1",
+                "doctrine/inflector": "^1.4|^2.0",
                 "dragonmantank/cron-expression": "^2.0",
                 "egulias/email-validator": "^2.1.10",
                 "ext-json": "*",
                 "ext-mbstring": "*",
                 "ext-openssl": "*",
                 "league/commonmark": "^1.3",
-                "league/flysystem": "^1.0.8",
+                "league/flysystem": "^1.0.34",
                 "monolog/monolog": "^1.12|^2.0",
                 "nesbot/carbon": "^2.0",
                 "opis/closure": "^3.1",
                 "symfony/finder": "^4.3.4",
                 "symfony/http-foundation": "^4.3.4",
                 "symfony/http-kernel": "^4.3.4",
+                "symfony/polyfill-php73": "^1.17",
                 "symfony/process": "^4.3.4",
                 "symfony/routing": "^4.3.4",
                 "symfony/var-dumper": "^4.3.4",
             "suggest": {
                 "aws/aws-sdk-php": "Required to use the SQS queue driver, DynamoDb failed job storage and SES mail driver (^3.0).",
                 "doctrine/dbal": "Required to rename columns and drop SQLite columns (^2.6).",
+                "ext-ftp": "Required to use the Flysystem FTP driver.",
                 "ext-gd": "Required to use Illuminate\\Http\\Testing\\FileFactory::image().",
                 "ext-memcached": "Required to use the memcache cache driver.",
                 "ext-pcntl": "Required to use all features of the queue worker.",
                 "moontoast/math": "Required to use ordered UUIDs (^1.1).",
                 "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
                 "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
+                "predis/predis": "Required to use the predis connector (^1.1.2).",
                 "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
                 "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).",
                 "symfony/cache": "Required to PSR-6 cache bridge (^4.3.4).",
                 "framework",
                 "laravel"
             ],
-            "time": "2020-03-24T16:37:50+00:00"
+            "time": "2020-09-09T15:02:20+00:00"
         },
         {
             "name": "laravel/socialite",
-            "version": "v4.3.2",
+            "version": "v4.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/socialite.git",
-                "reference": "4bd66ee416fea04398dee5b8c32d65719a075db4"
+                "reference": "80951df0d93435b773aa00efe1fad6d5015fac75"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/4bd66ee416fea04398dee5b8c32d65719a075db4",
-                "reference": "4bd66ee416fea04398dee5b8c32d65719a075db4",
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/80951df0d93435b773aa00efe1fad6d5015fac75",
+                "reference": "80951df0d93435b773aa00efe1fad6d5015fac75",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "guzzlehttp/guzzle": "~6.0",
+                "guzzlehttp/guzzle": "^6.0|^7.0",
                 "illuminate/http": "~5.7.0|~5.8.0|^6.0|^7.0",
                 "illuminate/support": "~5.7.0|~5.8.0|^6.0|^7.0",
-                "league/oauth1-client": "~1.0",
+                "league/oauth1-client": "^1.0",
                 "php": "^7.1.3"
             },
             "require-dev": {
                 "illuminate/contracts": "~5.7.0|~5.8.0|^6.0|^7.0",
                 "mockery/mockery": "^1.0",
+                "orchestra/testbench": "^3.7|^3.8|^4.0|^5.0",
                 "phpunit/phpunit": "^7.0|^8.0"
             },
             "type": "library",
                 "laravel",
                 "oauth"
             ],
-            "time": "2020-02-04T15:30:01+00:00"
+            "time": "2020-06-03T13:30:03+00:00"
         },
         {
             "name": "league/commonmark",
-            "version": "1.4.2",
+            "version": "1.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/commonmark.git",
-                "reference": "9e780d972185e4f737a03bade0fd34a9e67bbf31"
+                "reference": "45832dfed6007b984c0d40addfac48d403dc6432"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/9e780d972185e4f737a03bade0fd34a9e67bbf31",
-                "reference": "9e780d972185e4f737a03bade0fd34a9e67bbf31",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/45832dfed6007b984c0d40addfac48d403dc6432",
+                "reference": "45832dfed6007b984c0d40addfac48d403dc6432",
                 "shasum": ""
             },
             "require": {
                 "ext-mbstring": "*",
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "conflict": {
                 "scrutinizer/ocular": "1.7.*"
             },
             "require-dev": {
                 "cebe/markdown": "~1.0",
-                "commonmark/commonmark.js": "0.29.1",
+                "commonmark/commonmark.js": "0.29.2",
                 "erusev/parsedown": "~1.0",
                 "ext-json": "*",
                 "github/gfm": "0.29.0",
                 "michelf/php-markdown": "~1.4",
                 "mikehaertl/php-shellcommand": "^1.4",
                 "phpstan/phpstan": "^0.12",
-                "phpunit/phpunit": "^7.5",
+                "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
                 "scrutinizer/ocular": "^1.5",
                 "symfony/finder": "^4.2"
             },
                 "bin/commonmark"
             ],
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.4-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "League\\CommonMark\\": "src"
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-04-24T13:39:56+00:00"
+            "time": "2020-09-13T14:44:46+00:00"
         },
         {
             "name": "league/flysystem",
-            "version": "1.0.66",
+            "version": "1.0.70",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21"
+                "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/021569195e15f8209b1c4bebb78bd66aa4f08c21",
-                "reference": "021569195e15f8209b1c4bebb78bd66aa4f08c21",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/585824702f534f8d3cf7fab7225e8466cc4b7493",
+                "reference": "585824702f534f8d3cf7fab7225e8466cc4b7493",
                 "shasum": ""
             },
             "require": {
                 "league/flysystem-sftp": "<1.0.6"
             },
             "require-dev": {
-                "phpspec/phpspec": "^3.4",
+                "phpspec/phpspec": "^3.4 || ^4.0 || ^5.0 || ^6.0",
                 "phpunit/phpunit": "^5.7.26"
             },
             "suggest": {
                 "sftp",
                 "storage"
             ],
-            "time": "2020-03-17T18:58:12+00:00"
+            "funding": [
+                {
+                    "url": "https://offset.earth/frankdejonge",
+                    "type": "other"
+                }
+            ],
+            "time": "2020-07-26T07:20:36+00:00"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
-            "version": "1.0.24",
+            "version": "1.0.28",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem-aws-s3-v3.git",
-                "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570"
+                "reference": "af7384a12f7cd7d08183390d930c9d0ec629c990"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/4382036bde5dc926f9b8b337e5bdb15e5ec7b570",
-                "reference": "4382036bde5dc926f9b8b337e5bdb15e5ec7b570",
+                "url": "https://api.github.com/repos/thephpleague/flysystem-aws-s3-v3/zipball/af7384a12f7cd7d08183390d930c9d0ec629c990",
+                "reference": "af7384a12f7cd7d08183390d930c9d0ec629c990",
                 "shasum": ""
             },
             "require": {
-                "aws/aws-sdk-php": "^3.0.0",
+                "aws/aws-sdk-php": "^3.20.0",
                 "league/flysystem": "^1.0.40",
                 "php": ">=5.5.0"
             },
                 }
             ],
             "description": "Flysystem adapter for the AWS S3 SDK v3.x",
-            "time": "2020-02-23T13:31:58+00:00"
+            "time": "2020-08-22T08:43:01+00:00"
         },
         {
             "name": "league/oauth1-client",
-            "version": "1.7.0",
+            "version": "v1.8.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/oauth1-client.git",
-                "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647"
+                "reference": "3a68155c3f27a91f4b66a2dc03996cd6f3281c9f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/fca5f160650cb74d23fc11aa570dd61f86dcf647",
-                "reference": "fca5f160650cb74d23fc11aa570dd61f86dcf647",
+                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/3a68155c3f27a91f4b66a2dc03996cd6f3281c9f",
+                "reference": "3a68155c3f27a91f4b66a2dc03996cd6f3281c9f",
                 "shasum": ""
             },
             "require": {
-                "guzzlehttp/guzzle": "^6.0",
-                "php": ">=5.5.0"
+                "ext-json": "*",
+                "ext-openssl": "*",
+                "guzzlehttp/guzzle": "^6.0|^7.0",
+                "php": ">=7.1"
             },
             "require-dev": {
-                "mockery/mockery": "^0.9",
-                "phpunit/phpunit": "^4.0",
-                "squizlabs/php_codesniffer": "^2.0"
+                "ext-simplexml": "*",
+                "friendsofphp/php-cs-fixer": "^2.16.1",
+                "mockery/mockery": "^1.3",
+                "phpstan/phpstan": "^0.12.42",
+                "phpunit/phpunit": "^7.5"
+            },
+            "suggest": {
+                "ext-simplexml": "For decoding XML-based responses."
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0-dev"
+                    "dev-master": "1.0-dev",
+                    "dev-develop": "2.0-dev"
                 }
             },
             "autoload": {
                 "psr-4": {
-                    "League\\OAuth1\\": "src/"
+                    "League\\OAuth1\\Client\\": "src/"
                 }
             },
             "notification-url": "https://packagist.org/downloads/",
                 "tumblr",
                 "twitter"
             ],
-            "time": "2016-08-17T00:36:58+00:00"
+            "time": "2020-09-04T11:07:03+00:00"
         },
         {
             "name": "monolog/monolog",
-            "version": "2.0.2",
+            "version": "2.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8"
+                "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/c861fcba2ca29404dc9e617eedd9eff4616986b8",
-                "reference": "c861fcba2ca29404dc9e617eedd9eff4616986b8",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f9eee5cec93dfb313a38b6b288741e84e53f02d5",
+                "reference": "f9eee5cec93dfb313a38b6b288741e84e53f02d5",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2",
+                "php": ">=7.2",
                 "psr/log": "^1.0.1"
             },
             "provide": {
                 "doctrine/couchdb": "~1.0@dev",
                 "elasticsearch/elasticsearch": "^6.0",
                 "graylog2/gelf-php": "^1.4.2",
-                "jakub-onderka/php-parallel-lint": "^0.9",
                 "php-amqplib/php-amqplib": "~2.4",
                 "php-console/php-console": "^3.1.3",
+                "php-parallel-lint/php-parallel-lint": "^1.0",
                 "phpspec/prophecy": "^1.6.1",
-                "phpunit/phpunit": "^8.3",
+                "phpunit/phpunit": "^8.5",
                 "predis/predis": "^1.1",
                 "rollbar/rollbar": "^1.3",
                 "ruflin/elastica": ">=0.90 <3.0",
                 "logging",
                 "psr-3"
             ],
-            "time": "2019-12-20T14:22:59+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/monolog/monolog",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-23T08:41:23+00:00"
         },
         {
             "name": "mtdowling/jmespath.php",
-            "version": "2.5.0",
+            "version": "2.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/jmespath/jmespath.php.git",
-                "reference": "52168cb9472de06979613d365c7f1ab8798be895"
+                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/52168cb9472de06979613d365c7f1ab8798be895",
-                "reference": "52168cb9472de06979613d365c7f1ab8798be895",
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb",
+                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.4.0",
-                "symfony/polyfill-mbstring": "^1.4"
+                "php": "^5.4 || ^7.0 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.17"
             },
             "require-dev": {
-                "composer/xdebug-handler": "^1.2",
-                "phpunit/phpunit": "^4.8.36|^7.5.15"
+                "composer/xdebug-handler": "^1.4",
+                "phpunit/phpunit": "^4.8.36 || ^7.5.15"
             },
             "bin": [
                 "bin/jp.php"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.5-dev"
+                    "dev-master": "2.6-dev"
                 }
             },
             "autoload": {
                 "json",
                 "jsonpath"
             ],
-            "time": "2019-12-30T18:03:34+00:00"
+            "time": "2020-07-31T21:01:56+00:00"
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.32.2",
+            "version": "2.40.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc"
+                "reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/f10e22cf546704fab1db4ad4b9dedbc5c797a0dc",
-                "reference": "f10e22cf546704fab1db4ad4b9dedbc5c797a0dc",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/6c7646154181013ecd55e80c201b9fd873c6ee5d",
+                "reference": "6c7646154181013ecd55e80c201b9fd873c6ee5d",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "php": "^7.1.8 || ^8.0",
+                "symfony/polyfill-mbstring": "^1.0",
                 "symfony/translation": "^3.4 || ^4.0 || ^5.0"
             },
             "require-dev": {
                 "doctrine/orm": "^2.7",
                 "friendsofphp/php-cs-fixer": "^2.14 || ^3.0",
-                "kylekatarnls/multi-tester": "^1.1",
-                "phpmd/phpmd": "^2.8",
-                "phpstan/phpstan": "^0.11",
+                "kylekatarnls/multi-tester": "^2.0",
+                "phpmd/phpmd": "^2.9",
+                "phpstan/extension-installer": "^1.0",
+                "phpstan/phpstan": "^0.12.35",
                 "phpunit/phpunit": "^7.5 || ^8.0",
                 "squizlabs/php_codesniffer": "^3.4"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.x-dev"
+                    "dev-master": "2.x-dev",
+                    "dev-3.x": "3.x-dev"
                 },
                 "laravel": {
                     "providers": [
                         "Carbon\\Laravel\\ServiceProvider"
                     ]
+                },
+                "phpstan": {
+                    "includes": [
+                        "extension.neon"
+                    ]
                 }
             },
             "autoload": {
                 "datetime",
                 "time"
             ],
-            "time": "2020-03-31T13:43:19+00:00"
+            "funding": [
+                {
+                    "url": "https://opencollective.com/Carbon",
+                    "type": "open_collective"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-11T19:00:58+00:00"
         },
         {
             "name": "nunomaduro/collision",
         },
         {
             "name": "opis/closure",
-            "version": "3.5.1",
+            "version": "3.5.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/opis/closure.git",
-                "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969"
+                "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/opis/closure/zipball/93ebc5712cdad8d5f489b500c59d122df2e53969",
-                "reference": "93ebc5712cdad8d5f489b500c59d122df2e53969",
+                "url": "https://api.github.com/repos/opis/closure/zipball/4531e53afe2fc660403e76fb7644e95998bff7bf",
+                "reference": "4531e53afe2fc660403e76fb7644e95998bff7bf",
                 "shasum": ""
             },
             "require": {
                 "serialization",
                 "serialize"
             ],
-            "time": "2019-11-29T22:36:02+00:00"
+            "time": "2020-09-06T17:02:15+00:00"
         },
         {
             "name": "paragonie/random_compat",
         },
         {
             "name": "phenx/php-font-lib",
-            "version": "0.5.1",
+            "version": "0.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/PhenX/php-font-lib.git",
-                "reference": "760148820110a1ae0936e5cc35851e25a938bc97"
+                "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/760148820110a1ae0936e5cc35851e25a938bc97",
-                "reference": "760148820110a1ae0936e5cc35851e25a938bc97",
+                "url": "https://api.github.com/repos/PhenX/php-font-lib/zipball/ca6ad461f032145fff5971b5985e5af9e7fa88d8",
+                "reference": "ca6ad461f032145fff5971b5985e5af9e7fa88d8",
                 "shasum": ""
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8"
+                "phpunit/phpunit": "^4.8.35 || ^5 || ^6 || ^7"
             },
             "type": "library",
             "autoload": {
             ],
             "description": "A library to read, parse, export and make subsets of different types of font files.",
             "homepage": "https://github.com/PhenX/php-font-lib",
-            "time": "2017-09-13T16:14:37+00:00"
+            "time": "2020-03-08T15:31:32+00:00"
         },
         {
             "name": "phenx/php-svg-lib",
         },
         {
             "name": "phpoption/phpoption",
-            "version": "1.7.3",
+            "version": "1.7.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/schmittjoh/php-option.git",
-                "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae"
+                "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
-                "reference": "4acfd6a4b33a509d8c88f50e5222f734b6aeebae",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/994ecccd8f3283ecf5ac33254543eb0ac946d525",
+                "reference": "994ecccd8f3283ecf5ac33254543eb0ac946d525",
                 "shasum": ""
             },
             "require": {
                 "php": "^5.5.9 || ^7.0 || ^8.0"
             },
             "require-dev": {
-                "bamarni/composer-bin-plugin": "^1.3",
-                "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
+                "bamarni/composer-bin-plugin": "^1.4.1",
+                "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0 || ^8.0 || ^9.0"
             },
             "type": "library",
             "extra": {
                 "php",
                 "type"
             ],
-            "time": "2020-03-21T18:07:53+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-20T17:29:33+00:00"
         },
         {
             "name": "predis/predis",
-            "version": "v1.1.1",
+            "version": "v1.1.6",
             "source": {
                 "type": "git",
-                "url": "https://github.com/nrk/predis.git",
-                "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1"
+                "url": "https://github.com/predis/predis.git",
+                "reference": "9930e933c67446962997b05201c69c2319bf26de"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nrk/predis/zipball/f0210e38881631afeafb56ab43405a92cafd9fd1",
-                "reference": "f0210e38881631afeafb56ab43405a92cafd9fd1",
+                "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de",
+                "reference": "9930e933c67446962997b05201c69c2319bf26de",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.9"
             },
             "require-dev": {
+                "cweagans/composer-patches": "^1.6",
                 "phpunit/phpunit": "~4.8"
             },
             "suggest": {
                 "ext-phpiredis": "Allows faster serialization and deserialization of the Redis protocol"
             },
             "type": "library",
+            "extra": {
+                "composer-exit-on-patch-failure": true,
+                "patches": {
+                    "phpunit/phpunit-mock-objects": {
+                        "Fix PHP 7 and 8 compatibility": "./tests/phpunit_mock_objects.patch"
+                    },
+                    "phpunit/phpunit": {
+                        "Fix PHP 7 compatibility": "./tests/phpunit_php7.patch",
+                        "Fix PHP 8 compatibility": "./tests/phpunit_php8.patch"
+                    }
+                }
+            },
             "autoload": {
                 "psr-4": {
                     "Predis\\": "src/"
                 {
                     "name": "Daniele Alessandri",
                     "email": "[email protected]",
-                    "homepage": "http://clorophilla.net"
+                    "homepage": "http://clorophilla.net",
+                    "role": "Creator & Maintainer"
+                },
+                {
+                    "name": "Till Krüss",
+                    "homepage": "https://till.im",
+                    "role": "Maintainer"
                 }
             ],
             "description": "Flexible and feature-complete Redis client for PHP and HHVM",
-            "homepage": "http://github.com/nrk/predis",
+            "homepage": "http://github.com/predis/predis",
             "keywords": [
                 "nosql",
                 "predis",
                 "redis"
             ],
-            "time": "2016-06-16T16:22:20+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/sponsors/tillkruss",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-11T19:18:05+00:00"
         },
         {
             "name": "psr/container",
         },
         {
             "name": "robrichards/xmlseclibs",
-            "version": "3.0.4",
+            "version": "3.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/robrichards/xmlseclibs.git",
-                "reference": "0a53d3c3aa87564910cae4ed01416441d3ae0db5"
+                "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/0a53d3c3aa87564910cae4ed01416441d3ae0db5",
-                "reference": "0a53d3c3aa87564910cae4ed01416441d3ae0db5",
+                "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/f8f19e58f26cdb42c54b214ff8a820760292f8df",
+                "reference": "f8f19e58f26cdb42c54b214ff8a820760292f8df",
                 "shasum": ""
             },
             "require": {
                 "xml",
                 "xmldsig"
             ],
-            "time": "2019-11-05T11:44:22+00:00"
+            "time": "2020-09-05T13:00:25+00:00"
         },
         {
             "name": "sabberworm/php-css-parser",
-            "version": "8.3.0",
+            "version": "8.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
-                "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f"
+                "reference": "d217848e1396ef962fb1997cf3e2421acba7f796"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f",
-                "reference": "91bcc3e3fdb7386c9a2e0e0aa09ca75cc43f121f",
+                "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/d217848e1396ef962fb1997cf3e2421acba7f796",
+                "reference": "d217848e1396ef962fb1997cf3e2421acba7f796",
                 "shasum": ""
             },
             "require": {
                 "parser",
                 "stylesheet"
             ],
-            "time": "2019-02-22T07:42:52+00:00"
+            "time": "2020-06-01T09:10:00+00:00"
         },
         {
             "name": "scrivo/highlight.php",
-            "version": "v9.18.1.1",
+            "version": "v9.18.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/scrivo/highlight.php.git",
-                "reference": "52fc21c99fd888e33aed4879e55a3646f8d40558"
+                "reference": "efb6e445494a9458aa59b0af5edfa4bdcc6809d9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/52fc21c99fd888e33aed4879e55a3646f8d40558",
-                "reference": "52fc21c99fd888e33aed4879e55a3646f8d40558",
+                "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/efb6e445494a9458aa59b0af5edfa4bdcc6809d9",
+                "reference": "efb6e445494a9458aa59b0af5edfa4bdcc6809d9",
                 "shasum": ""
             },
             "require": {
                 "highlight.php",
                 "syntax"
             ],
-            "time": "2020-03-02T05:59:21+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/allejo",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-08-27T03:24:44+00:00"
         },
         {
             "name": "socialiteproviders/discord",
         },
         {
             "name": "socialiteproviders/manager",
-            "version": "v3.5",
+            "version": "v3.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Manager.git",
-                "reference": "7a5872d9e4b22bb26ecd0c69ea9ddbaad8c0f570"
+                "reference": "fc8dbcf0061f12bfe0cc347e9655af932860ad36"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/7a5872d9e4b22bb26ecd0c69ea9ddbaad8c0f570",
-                "reference": "7a5872d9e4b22bb26ecd0c69ea9ddbaad8c0f570",
+                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/fc8dbcf0061f12bfe0cc347e9655af932860ad36",
+                "reference": "fc8dbcf0061f12bfe0cc347e9655af932860ad36",
                 "shasum": ""
             },
             "require": {
-                "illuminate/support": "~5.4|~5.7.0|~5.8.0|^6.0|^7.0",
-                "laravel/socialite": "~3.0|~4.0",
-                "php": "^5.6 || ^7.0"
+                "illuminate/support": "^6.0|^7.0|^8.0",
+                "laravel/socialite": "~4.0|~5.0",
+                "php": "^7.2"
             },
             "require-dev": {
-                "mockery/mockery": "^0.9.4",
-                "phpunit/phpunit": "^5.0"
+                "mockery/mockery": "^1.2",
+                "phpunit/phpunit": "^8.0"
             },
             "type": "library",
             "extra": {
                 {
                     "name": "Miguel Piedrafita",
                     "email": "[email protected]"
+                },
+                {
+                    "name": "atymic",
+                    "email": "[email protected]",
+                    "homepage": "https://atymic.dev"
                 }
             ],
             "description": "Easily add new or override built-in providers in Laravel Socialite.",
-            "time": "2020-03-08T16:54:44+00:00"
+            "homepage": "https://socialiteproviders.com/",
+            "time": "2020-09-08T10:41:06+00:00"
         },
         {
             "name": "socialiteproviders/microsoft-azure",
-            "version": "v3.0.0",
+            "version": "v3.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
-                "reference": "d7a703a782eb9f7eae0db803beaa3ddec19ef372"
+                "reference": "b22f4696cccecd6de902cf0bc923de7fc2e4608e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/d7a703a782eb9f7eae0db803beaa3ddec19ef372",
-                "reference": "d7a703a782eb9f7eae0db803beaa3ddec19ef372",
+                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/b22f4696cccecd6de902cf0bc923de7fc2e4608e",
+                "reference": "b22f4696cccecd6de902cf0bc923de7fc2e4608e",
                 "shasum": ""
             },
             "require": {
+                "ext-json": "*",
                 "php": "^5.6 || ^7.0",
-                "socialiteproviders/manager": "~3.0"
+                "socialiteproviders/manager": "~2.0 || ~3.0"
             },
             "type": "library",
             "autoload": {
                 }
             ],
             "description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
-            "time": "2017-01-25T09:48:29+00:00"
+            "time": "2020-04-30T23:01:40+00:00"
         },
         {
             "name": "socialiteproviders/okta",
-            "version": "v1.0.0",
+            "version": "v1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Okta.git",
-                "reference": "dcda13432c80060cd84d4cb5f2af422d280ab895"
+                "reference": "7c2512f0872316b139e3eea1c50c9351747a57ea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/dcda13432c80060cd84d4cb5f2af422d280ab895",
-                "reference": "dcda13432c80060cd84d4cb5f2af422d280ab895",
+                "url": "https://api.github.com/repos/SocialiteProviders/Okta/zipball/7c2512f0872316b139e3eea1c50c9351747a57ea",
+                "reference": "7c2512f0872316b139e3eea1c50c9351747a57ea",
                 "shasum": ""
             },
             "require": {
+                "ext-json": "*",
                 "php": "^5.6 || ^7.0",
                 "socialiteproviders/manager": "~2.0 || ~3.0"
             },
                 }
             ],
             "description": "Okta OAuth2 Provider for Laravel Socialite",
-            "time": "2017-11-21T05:31:47+00:00"
+            "time": "2019-09-06T15:27:03+00:00"
         },
         {
             "name": "socialiteproviders/slack",
         },
         {
             "name": "socialiteproviders/twitch",
-            "version": "v5.1",
+            "version": "v5.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Twitch.git",
-                "reference": "f9b1f90a94f539e1b29e84ee0f731f42d59f3213"
+                "reference": "9ee6fe196d7c28777139b3cde04cbd537cf7e652"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/f9b1f90a94f539e1b29e84ee0f731f42d59f3213",
-                "reference": "f9b1f90a94f539e1b29e84ee0f731f42d59f3213",
+                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/9ee6fe196d7c28777139b3cde04cbd537cf7e652",
+                "reference": "9ee6fe196d7c28777139b3cde04cbd537cf7e652",
                 "shasum": ""
             },
             "require": {
+                "ext-json": "*",
                 "php": "^5.6 || ^7.0",
                 "socialiteproviders/manager": "~2.0 || ~3.0"
             },
                 }
             ],
             "description": "Twitch OAuth2 Provider for Laravel Socialite",
-            "time": "2019-07-01T10:35:46+00:00"
+            "time": "2020-05-06T22:51:30+00:00"
         },
         {
             "name": "swiftmailer/swiftmailer",
         },
         {
             "name": "symfony/console",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7"
+                "reference": "b39fd99b9297b67fb7633b7d8083957a97e1e727"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
-                "reference": "10bb3ee3c97308869d53b3e3d03f6ac23ff985f7",
+                "url": "https://api.github.com/repos/symfony/console/zipball/b39fd99b9297b67fb7633b7d8083957a97e1e727",
+                "reference": "b39fd99b9297b67fb7633b7d8083957a97e1e727",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
                 "symfony/polyfill-php73": "^1.8",
+                "symfony/polyfill-php80": "^1.15",
                 "symfony/service-contracts": "^1.1|^2"
             },
             "conflict": {
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-30T11:41:10+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-02T07:07:21+00:00"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b"
+                "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/afc26133a6fbdd4f8842e38893e0ee4685c7c94b",
-                "reference": "afc26133a6fbdd4f8842e38893e0ee4685c7c94b",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
+                "reference": "bf17dc9f6ce144e41f786c32435feea4d8e11dcc",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Symfony CssSelector Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-05T09:39:30+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "346636d2cae417992ecfd761979b2ab98b339a45"
+                "reference": "aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/346636d2cae417992ecfd761979b2ab98b339a45",
-                "reference": "346636d2cae417992ecfd761979b2ab98b339a45",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e",
+                "reference": "aeb73aca16a8f1fe958230fe44e6cf4c84cbb85e",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
-                "psr/log": "~1.0"
+                "php": ">=7.1.3",
+                "psr/log": "~1.0",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "symfony/http-kernel": "<3.4"
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-10T07:47:39+00:00"
         },
         {
             "name": "symfony/error-handler",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/error-handler.git",
-                "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358"
+                "reference": "2434fb32851f252e4f27691eee0b77c16198db62"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/error-handler/zipball/7e9828fc98aa1cf27b422fe478a84f5b0abb7358",
-                "reference": "7e9828fc98aa1cf27b422fe478a84f5b0abb7358",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/2434fb32851f252e4f27691eee0b77c16198db62",
+                "reference": "2434fb32851f252e4f27691eee0b77c16198db62",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "psr/log": "~1.0",
                 "symfony/debug": "^4.4.5",
+                "symfony/polyfill-php80": "^1.15",
                 "symfony/var-dumper": "^4.4|^5.0"
             },
             "require-dev": {
             ],
             "description": "Symfony ErrorHandler Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-30T14:07:33+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T09:56:45+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed"
+                "reference": "3e8ea5ccddd00556b86d69d42f99f1061a704030"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/abc8e3618bfdb55e44c8c6a00abd333f831bbfed",
-                "reference": "abc8e3618bfdb55e44c8c6a00abd333f831bbfed",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/3e8ea5ccddd00556b86d69d42f99f1061a704030",
+                "reference": "3e8ea5ccddd00556b86d69d42f99f1061a704030",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/event-dispatcher-contracts": "^1.1"
             },
             "conflict": {
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-13T14:18:44+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
-            "version": "v1.1.7",
+            "version": "v1.1.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher-contracts.git",
-                "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18"
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18",
-                "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/84e23fdcd2517bf37aecbd16967e83f0caee25a7",
+                "reference": "84e23fdcd2517bf37aecbd16967e83f0caee25a7",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "suggest": {
                 "psr/event-dispatcher": "",
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-09-17T09:54:03+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-06T13:19:58+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "5729f943f9854c5781984ed4907bbb817735776b"
+                "reference": "2a78590b2c7e3de5c429628457c47541c58db9c7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/5729f943f9854c5781984ed4907bbb817735776b",
-                "reference": "5729f943f9854c5781984ed4907bbb817735776b",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/2a78590b2c7e3de5c429628457c47541c58db9c7",
+                "reference": "2a78590b2c7e3de5c429628457c47541c58db9c7",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T09:56:45+00:00"
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b"
+                "reference": "e3e5a62a6631a461954d471e7206e3750dbe8ee1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/62f92509c9abfd1f73e17b8cf1b72c0bdac6611b",
-                "reference": "62f92509c9abfd1f73e17b8cf1b72c0bdac6611b",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/e3e5a62a6631a461954d471e7206e3750dbe8ee1",
+                "reference": "e3e5a62a6631a461954d471e7206e3750dbe8ee1",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/mime": "^4.3|^5.0",
                 "symfony/polyfill-mbstring": "~1.1"
             },
             ],
             "description": "Symfony HttpFoundation Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-30T14:07:33+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T07:39:58+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc"
+                "reference": "2bb7b90ecdc79813c0bf237b7ff20e79062b5188"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/f356a489e51856b99908005eb7f2c51a1dfc95dc",
-                "reference": "f356a489e51856b99908005eb7f2c51a1dfc95dc",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/2bb7b90ecdc79813c0bf237b7ff20e79062b5188",
+                "reference": "2bb7b90ecdc79813c0bf237b7ff20e79062b5188",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "psr/log": "~1.0",
                 "symfony/error-handler": "^4.4",
                 "symfony/event-dispatcher": "^4.4",
                 "symfony/http-foundation": "^4.4|^5.0",
                 "symfony/polyfill-ctype": "^1.8",
-                "symfony/polyfill-php73": "^1.9"
+                "symfony/polyfill-php73": "^1.9",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "symfony/browser-kit": "<4.3",
             ],
             "description": "Symfony HttpKernel Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-30T14:59:15+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-02T08:09:29+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "6dde9dc70155e91b850b1d009d1f841c54bc4aba"
+                "reference": "50ad671306d3d3ffb888d95b4fb1859496831e3a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/6dde9dc70155e91b850b1d009d1f841c54bc4aba",
-                "reference": "6dde9dc70155e91b850b1d009d1f841c54bc4aba",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/50ad671306d3d3ffb888d95b4fb1859496831e3a",
+                "reference": "50ad671306d3d3ffb888d95b4fb1859496831e3a",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-intl-idn": "^1.10",
                 "symfony/polyfill-mbstring": "^1.0"
             },
                 "mime",
                 "mime-type"
             ],
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T09:56:45+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14"
+                "reference": "1c302646f6efc070cd46856e600e5e0684d6b454"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
-                "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/1c302646f6efc070cd46856e600e5e0684d6b454",
+                "reference": "1c302646f6efc070cd46856e600e5e0684d6b454",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "polyfill",
                 "portable"
             ],
-            "time": "2020-02-27T09:26:54+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/polyfill-iconv",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-iconv.git",
-                "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8"
+                "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/ad6d62792bfbcfc385dd34b424d4fcf9712a32c8",
-                "reference": "ad6d62792bfbcfc385dd34b424d4fcf9712a32c8",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36",
+                "reference": "6c2f78eb8f5ab8eaea98f6d414a5915f2e0fce36",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2020-03-09T19:04:49+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/polyfill-intl-idn",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-idn.git",
-                "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf"
+                "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
-                "reference": "47bd6aa45beb1cd7c6a16b7d1810133b728bdfcf",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/5dcab1bc7146cf8c1beaa4502a3d9be344334251",
+                "reference": "5dcab1bc7146cf8c1beaa4502a3d9be344334251",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3",
-                "symfony/polyfill-mbstring": "^1.3",
+                "symfony/polyfill-intl-normalizer": "^1.10",
+                "symfony/polyfill-php70": "^1.10",
                 "symfony/polyfill-php72": "^1.10"
             },
             "suggest": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                     "name": "Laurent Bassin",
                     "email": "[email protected]"
                 },
+                {
+                    "name": "Trevor Rowbotham",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Symfony Community",
                     "homepage": "https://symfony.com/contributors"
                 "portable",
                 "shim"
             ],
-            "time": "2020-03-09T19:04:49+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-04T06:02:08+00:00"
+        },
+        {
+            "name": "symfony/polyfill-intl-normalizer",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
+                "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
+                "reference": "37078a8dd4a2a1e9ab0231af7c6cb671b2ed5a7e",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-intl": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Intl\\Normalizer\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for intl's Normalizer class and related functions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "intl",
+                "normalizer",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac"
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
-                "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/a6977d63bf9a0ad4c65cd352709e230876f9904a",
+                "reference": "a6977d63bf9a0ad4c65cd352709e230876f9904a",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2020-03-09T19:04:49+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php70",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php70.git",
+                "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
+                "reference": "0dd93f2c578bdc9c72697eaa5f1dd25644e618d3",
+                "shasum": ""
+            },
+            "require": {
+                "paragonie/random_compat": "~1.0|~2.0|~9.99",
+                "php": ">=5.3.3"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php70\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "37b0976c78b94856543260ce09b460a7bc852747"
+                "reference": "639447d008615574653fb3bc60d1986d7172eaae"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/37b0976c78b94856543260ce09b460a7bc852747",
-                "reference": "37b0976c78b94856543260ce09b460a7bc852747",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/639447d008615574653fb3bc60d1986d7172eaae",
+                "reference": "639447d008615574653fb3bc60d1986d7172eaae",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                 "portable",
                 "shim"
             ],
-            "time": "2020-02-27T09:26:54+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.15.0",
+            "version": "v1.18.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7"
+                "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7",
-                "reference": "0f27e9f464ea3da33cbe7ca3bdf4eb66def9d0f7",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
+                "reference": "fffa1a52a023e782cdcc221d781fe1ec8f87fcca",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.15-dev"
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
                 }
             },
             "autoload": {
                     "homepage": "https://symfony.com/contributors"
                 }
             ],
-            "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+            "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
+        },
+        {
+            "name": "symfony/polyfill-php80",
+            "version": "v1.18.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-php80.git",
+                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+                "reference": "d87d5766cbf48d72388a9f6b85f280c8ad51f981",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.8"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.18-dev"
+                },
+                "thanks": {
+                    "name": "symfony/polyfill",
+                    "url": "https://github.com/symfony/polyfill"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Php80\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ],
+                "classmap": [
+                    "Resources/stubs"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Ion Bazan",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "[email protected]"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions",
             "homepage": "https://symfony.com",
             "keywords": [
                 "compatibility",
                 "portable",
                 "shim"
             ],
-            "time": "2020-02-27T09:26:54+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T12:35:20+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3"
+                "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/3e40e87a20eaf83a1db825e1fa5097ae89042db3",
-                "reference": "3e40e87a20eaf83a1db825e1fa5097ae89042db3",
+                "url": "https://api.github.com/repos/symfony/process/zipball/65e70bab62f3da7089a8d4591fb23fbacacb3479",
+                "reference": "65e70bab62f3da7089a8d4591fb23fbacacb3479",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "type": "library",
             "extra": {
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-23T08:31:43+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8"
+                "reference": "e3387963565da9bae51d1d3ab8041646cc93bd04"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/0f562fa613e288d7dbae6c63abbc9b33ed75a8f8",
-                "reference": "0f562fa613e288d7dbae6c63abbc9b33ed75a8f8",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/e3387963565da9bae51d1d3ab8041646cc93bd04",
+                "reference": "e3387963565da9bae51d1d3ab8041646cc93bd04",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "conflict": {
                 "symfony/config": "<4.2",
                 "uri",
                 "url"
             ],
-            "time": "2020-03-30T11:41:10+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-10T07:27:51+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v1.1.8",
+            "version": "v1.1.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf"
+                "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
-                "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/b776d18b303a39f56c63747bcb977ad4b27aca26",
+                "reference": "b776d18b303a39f56c63747bcb977ad4b27aca26",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "psr/container": "^1.0"
             },
             "suggest": {
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-10-14T12:27:06+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-06T13:19:58+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "4e54d336f2eca5facad449d0b0118bb449375b76"
+                "reference": "700e6e50174b0cdcf0fa232773bec5c314680575"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/4e54d336f2eca5facad449d0b0118bb449375b76",
-                "reference": "4e54d336f2eca5facad449d0b0118bb449375b76",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/700e6e50174b0cdcf0fa232773bec5c314680575",
+                "reference": "700e6e50174b0cdcf0fa232773bec5c314680575",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
                 "symfony/translation-contracts": "^1.1.6|^2"
             },
             ],
             "description": "Symfony Translation Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T09:56:45+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v1.1.7",
+            "version": "v1.1.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6"
+                "reference": "84180a25fad31e23bebd26ca09d89464f082cacc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/364518c132c95642e530d9b2d217acbc2ccac3e6",
-                "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/84180a25fad31e23bebd26ca09d89464f082cacc",
+                "reference": "84180a25fad31e23bebd26ca09d89464f082cacc",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3"
+                "php": ">=7.1.3"
             },
             "suggest": {
                 "symfony/translation-implementation": ""
             "extra": {
                 "branch-alias": {
                     "dev-master": "1.1-dev"
+                },
+                "thanks": {
+                    "name": "symfony/contracts",
+                    "url": "https://github.com/symfony/contracts"
                 }
             },
             "autoload": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-09-17T11:12:18+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-02T16:08:58+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a"
+                "reference": "1bef32329f3166486ab7cb88599cae4875632b99"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/5a0c2d93006131a36cf6f767d10e2ca8333b0d4a",
-                "reference": "5a0c2d93006131a36cf6f767d10e2ca8333b0d4a",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/1bef32329f3166486ab7cb88599cae4875632b99",
+                "reference": "1bef32329f3166486ab7cb88599cae4875632b99",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-mbstring": "~1.0",
-                "symfony/polyfill-php72": "~1.5"
+                "symfony/polyfill-php72": "~1.5",
+                "symfony/polyfill-php80": "^1.15"
             },
             "conflict": {
                 "phpunit/phpunit": "<4.8.35|<5.4.3,>=5.0",
                 "debug",
                 "dump"
             ],
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-17T07:31:35+00:00"
         },
         {
             "name": "tijsverkoyen/css-to-inline-styles",
-            "version": "2.2.2",
+            "version": "2.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
-                "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15"
+                "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/dda2ee426acd6d801d5b7fd1001cde9b5f790e15",
-                "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15",
+                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/b43b05cf43c1b6d849478965062b6ef73e223bb5",
+                "reference": "b43b05cf43c1b6d849478965062b6ef73e223bb5",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-libxml": "*",
-                "php": "^5.5 || ^7.0",
+                "php": "^5.5 || ^7.0 || ^8.0",
                 "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^7.5"
             },
             "type": "library",
             "extra": {
             ],
             "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
             "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
-            "time": "2019-10-24T08:53:34+00:00"
+            "time": "2020-07-13T06:12:54+00:00"
         },
         {
             "name": "vlucas/phpdotenv",
-            "version": "v3.6.2",
+            "version": "v3.6.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/vlucas/phpdotenv.git",
-                "reference": "786a947e57086cf236cefdee80784634224b99fa"
+                "reference": "2065beda6cbe75e2603686907b2e45f6f3a5ad82"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/786a947e57086cf236cefdee80784634224b99fa",
-                "reference": "786a947e57086cf236cefdee80784634224b99fa",
+                "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/2065beda6cbe75e2603686907b2e45f6f3a5ad82",
+                "reference": "2065beda6cbe75e2603686907b2e45f6f3a5ad82",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.4 || ^7.0",
-                "phpoption/phpoption": "^1.5",
-                "symfony/polyfill-ctype": "^1.9"
+                "php": "^5.4 || ^7.0 || ^8.0",
+                "phpoption/phpoption": "^1.5.2",
+                "symfony/polyfill-ctype": "^1.17"
             },
             "require-dev": {
                 "ext-filter": "*",
                 "ext-pcre": "*",
-                "phpunit/phpunit": "^4.8.35 || ^5.0 || ^6.0 || ^7.0"
+                "phpunit/phpunit": "^4.8.35 || ^5.7.27 || ^6.5.6 || ^7.0"
             },
             "suggest": {
                 "ext-filter": "Required to use the boolean validator.",
                 "env",
                 "environment"
             ],
-            "time": "2020-03-27T23:36:02+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/GrahamCampbell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-14T19:04:52+00:00"
         }
     ],
     "packages-dev": [
         {
             "name": "barryvdh/laravel-debugbar",
-            "version": "v3.2.9",
+            "version": "v3.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-debugbar.git",
-                "reference": "42d5da5379a7860093f8e4032167e4cb5ebec180"
+                "reference": "233c10688f4c1a6e66ed2ef123038b1363d1bedc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/42d5da5379a7860093f8e4032167e4cb5ebec180",
-                "reference": "42d5da5379a7860093f8e4032167e4cb5ebec180",
+                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/233c10688f4c1a6e66ed2ef123038b1363d1bedc",
+                "reference": "233c10688f4c1a6e66ed2ef123038b1363d1bedc",
                 "shasum": ""
             },
             "require": {
-                "illuminate/routing": "^5.5|^6|^7",
-                "illuminate/session": "^5.5|^6|^7",
-                "illuminate/support": "^5.5|^6|^7",
-                "maximebf/debugbar": "^1.15.1",
-                "php": ">=7.0",
-                "symfony/debug": "^3|^4|^5",
-                "symfony/finder": "^3|^4|^5"
+                "illuminate/routing": "^6|^7|^8",
+                "illuminate/session": "^6|^7|^8",
+                "illuminate/support": "^6|^7|^8",
+                "maximebf/debugbar": "^1.16.3",
+                "php": ">=7.2",
+                "symfony/debug": "^4.3|^5",
+                "symfony/finder": "^4.3|^5"
             },
             "require-dev": {
-                "laravel/framework": "5.5.x"
+                "orchestra/testbench-dusk": "^4|^5|^6",
+                "phpunit/phpunit": "^8.5|^9.0",
+                "squizlabs/php_codesniffer": "^3.5"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "3.2-dev"
+                    "dev-master": "3.5-dev"
                 },
                 "laravel": {
                     "providers": [
                 "profiler",
                 "webprofiler"
             ],
-            "time": "2020-02-25T20:42:23+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-07T19:32:39+00:00"
         },
         {
             "name": "barryvdh/laravel-ide-helper",
-            "version": "v2.6.7",
+            "version": "v2.8.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-ide-helper.git",
-                "reference": "edd69c5e0508972c81f1f7173236de2459c45814"
+                "reference": "affa55122f83575888d4ebf1728992686e8223de"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/edd69c5e0508972c81f1f7173236de2459c45814",
-                "reference": "edd69c5e0508972c81f1f7173236de2459c45814",
+                "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/affa55122f83575888d4ebf1728992686e8223de",
+                "reference": "affa55122f83575888d4ebf1728992686e8223de",
                 "shasum": ""
             },
             "require": {
                 "barryvdh/reflection-docblock": "^2.0.6",
-                "composer/composer": "^1.6",
+                "composer/composer": "^1.6 || ^2.0@dev",
                 "doctrine/dbal": "~2.3",
-                "illuminate/console": "^5.5|^6|^7",
-                "illuminate/filesystem": "^5.5|^6|^7",
-                "illuminate/support": "^5.5|^6|^7",
-                "php": ">=7.2"
+                "ext-json": "*",
+                "illuminate/console": "^6 || ^7 || ^8",
+                "illuminate/filesystem": "^6 || ^7 || ^8",
+                "illuminate/support": "^6 || ^7 || ^8",
+                "php": ">=7.2",
+                "phpdocumentor/type-resolver": "^1.1.0"
             },
             "require-dev": {
-                "illuminate/config": "^5.5|^6|^7",
-                "illuminate/view": "^5.5|^6|^7",
+                "friendsofphp/php-cs-fixer": "^2",
+                "illuminate/config": "^6 || ^7 || ^8",
+                "illuminate/view": "^6 || ^7 || ^8",
                 "mockery/mockery": "^1.3",
-                "orchestra/testbench": "^3|^4",
-                "phpro/grumphp": "^0.17.1",
-                "squizlabs/php_codesniffer": "^3"
+                "orchestra/testbench": "^4 || ^5 || ^6",
+                "phpunit/phpunit": "^8.5 || ^9",
+                "spatie/phpunit-snapshot-assertions": "^1.4 || ^2.2 || ^3",
+                "vimeo/psalm": "^3.12"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.6-dev"
+                    "dev-master": "2.8-dev"
                 },
                 "laravel": {
                     "providers": [
                 "phpstorm",
                 "sublime"
             ],
-            "time": "2020-02-25T20:41:32+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/barryvdh",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-09-07T07:36:37+00:00"
         },
         {
             "name": "barryvdh/reflection-docblock",
         },
         {
             "name": "composer/ca-bundle",
-            "version": "1.2.6",
+            "version": "1.2.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/ca-bundle.git",
-                "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e"
+                "reference": "8a7ecad675253e4654ea05505233285377405215"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/47fe531de31fca4a1b997f87308e7d7804348f7e",
-                "reference": "47fe531de31fca4a1b997f87308e7d7804348f7e",
+                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/8a7ecad675253e4654ea05505233285377405215",
+                "reference": "8a7ecad675253e4654ea05505233285377405215",
                 "shasum": ""
             },
             "require": {
                 "ssl",
                 "tls"
             ],
-            "time": "2020-01-13T10:02:55+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-23T12:54:47+00:00"
         },
         {
             "name": "composer/composer",
-            "version": "1.10.1",
+            "version": "1.10.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/composer.git",
-                "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011"
+                "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/composer/zipball/b912a45da3e2b22f5cb5a23e441b697a295ba011",
-                "reference": "b912a45da3e2b22f5cb5a23e441b697a295ba011",
+                "url": "https://api.github.com/repos/composer/composer/zipball/47c841ba3b2d3fc0b4b13282cf029ea18b66d78b",
+                "reference": "47c841ba3b2d3fc0b4b13282cf029ea18b66d78b",
                 "shasum": ""
             },
             "require": {
                 "composer/semver": "^1.0",
                 "composer/spdx-licenses": "^1.2",
                 "composer/xdebug-handler": "^1.1",
-                "justinrainbow/json-schema": "^3.0 || ^4.0 || ^5.0",
+                "justinrainbow/json-schema": "^5.2.10",
                 "php": "^5.3.2 || ^7.0",
                 "psr/log": "^1.0",
                 "seld/jsonlint": "^1.4",
             },
             "require-dev": {
                 "phpspec/prophecy": "^1.10",
-                "symfony/phpunit-bridge": "^3.4"
+                "symfony/phpunit-bridge": "^4.2"
             },
             "suggest": {
                 "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
                 "dependency",
                 "package"
             ],
-            "time": "2020-03-13T19:34:27+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-09T09:46:34+00:00"
         },
         {
             "name": "composer/semver",
-            "version": "1.5.1",
+            "version": "1.7.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/semver.git",
-                "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de"
+                "reference": "114f819054a2ea7db03287f5efb757e2af6e4079"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
-                "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de",
+                "url": "https://api.github.com/repos/composer/semver/zipball/114f819054a2ea7db03287f5efb757e2af6e4079",
+                "reference": "114f819054a2ea7db03287f5efb757e2af6e4079",
                 "shasum": ""
             },
             "require": {
                 "validation",
                 "versioning"
             ],
-            "time": "2020-01-13T12:06:48+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-09-09T09:34:06+00:00"
         },
         {
             "name": "composer/spdx-licenses",
-            "version": "1.5.3",
+            "version": "1.5.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/spdx-licenses.git",
-                "reference": "0c3e51e1880ca149682332770e25977c70cf9dae"
+                "reference": "6946f785871e2314c60b4524851f3702ea4f2223"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/0c3e51e1880ca149682332770e25977c70cf9dae",
-                "reference": "0c3e51e1880ca149682332770e25977c70cf9dae",
+                "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/6946f785871e2314c60b4524851f3702ea4f2223",
+                "reference": "6946f785871e2314c60b4524851f3702ea4f2223",
                 "shasum": ""
             },
             "require": {
                 "spdx",
                 "validator"
             ],
-            "time": "2020-02-14T07:44:31+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-07-15T15:35:07+00:00"
         },
         {
             "name": "composer/xdebug-handler",
-            "version": "1.4.1",
+            "version": "1.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/xdebug-handler.git",
-                "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7"
+                "reference": "ebd27a9866ae8254e873866f795491f02418c5a5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7",
-                "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7",
+                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ebd27a9866ae8254e873866f795491f02418c5a5",
+                "reference": "ebd27a9866ae8254e873866f795491f02418c5a5",
                 "shasum": ""
             },
             "require": {
                 "Xdebug",
                 "performance"
             ],
-            "time": "2020-03-01T12:26:26+00:00"
+            "funding": [
+                {
+                    "url": "https://packagist.com",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/composer",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/composer/composer",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-19T10:27:58+00:00"
         },
         {
             "name": "doctrine/instantiator",
-            "version": "1.3.0",
+            "version": "1.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
+                "reference": "f350df0268e904597e3bd9c4685c53e0e333feea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
-                "reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/f350df0268e904597e3bd9c4685c53e0e333feea",
+                "reference": "f350df0268e904597e3bd9c4685c53e0e333feea",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "require-dev": {
                 "doctrine/coding-standard": "^6.0",
                 "constructor",
                 "instantiate"
             ],
-            "time": "2019-10-21T16:45:58+00:00"
+            "funding": [
+                {
+                    "url": "https://www.doctrine-project.org/sponsorship.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.patreon.com/phpdoctrine",
+                    "type": "patreon"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finstantiator",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-05-29T17:27:14+00:00"
         },
         {
             "name": "fzaninotto/faker",
         },
         {
             "name": "hamcrest/hamcrest-php",
-            "version": "v2.0.0",
+            "version": "v2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/hamcrest/hamcrest-php.git",
-                "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad"
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/776503d3a8e85d4f9a1148614f95b7a608b046ad",
-                "reference": "776503d3a8e85d4f9a1148614f95b7a608b046ad",
+                "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
+                "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3|^7.0"
+                "php": "^5.3|^7.0|^8.0"
             },
             "replace": {
                 "cordoval/hamcrest-php": "*",
                 "kodova/hamcrest-php": "*"
             },
             "require-dev": {
-                "phpunit/php-file-iterator": "1.3.3",
-                "phpunit/phpunit": "~4.0",
-                "satooshi/php-coveralls": "^1.0"
+                "phpunit/php-file-iterator": "^1.4 || ^2.0",
+                "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-master": "2.1-dev"
                 }
             },
             "autoload": {
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "BSD"
+                "BSD-3-Clause"
             ],
             "description": "This is the PHP port of Hamcrest Matchers",
             "keywords": [
                 "test"
             ],
-            "time": "2016-01-20T08:20:44+00:00"
+            "time": "2020-07-09T08:09:16+00:00"
         },
         {
             "name": "justinrainbow/json-schema",
-            "version": "5.2.9",
+            "version": "5.2.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/justinrainbow/json-schema.git",
-                "reference": "44c6787311242a979fa15c704327c20e7221a0e4"
+                "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4",
-                "reference": "44c6787311242a979fa15c704327c20e7221a0e4",
+                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
+                "reference": "2ba9c8c862ecd5510ed16c6340aa9f6eadb4f31b",
                 "shasum": ""
             },
             "require": {
                 "json",
                 "schema"
             ],
-            "time": "2019-09-25T14:49:45+00:00"
+            "time": "2020-05-27T16:41:55+00:00"
         },
         {
             "name": "laravel/browser-kit-testing",
-            "version": "v5.1.3",
+            "version": "v5.1.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/browser-kit-testing.git",
-                "reference": "cb0cf22cf38fe8796842adc8b9ad550ded2a1377"
+                "reference": "7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/cb0cf22cf38fe8796842adc8b9ad550ded2a1377",
-                "reference": "cb0cf22cf38fe8796842adc8b9ad550ded2a1377",
+                "url": "https://api.github.com/repos/laravel/browser-kit-testing/zipball/7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1",
+                "reference": "7664a30d2dbabcdb0315bfaa867fef2df8cb8fb1",
                 "shasum": ""
             },
             "require": {
                 "illuminate/support": "~5.7.0|~5.8.0|^6.0",
                 "mockery/mockery": "^1.0",
                 "php": ">=7.1.3",
-                "phpunit/phpunit": "^7.0|^8.0",
+                "phpunit/phpunit": "^7.5|^8.0",
                 "symfony/console": "^4.2",
                 "symfony/css-selector": "^4.2",
                 "symfony/dom-crawler": "^4.2",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "5.0-dev"
+                    "dev-master": "5.x-dev"
                 }
             },
             "autoload": {
                 "laravel",
                 "testing"
             ],
-            "time": "2019-07-30T14:57:44+00:00"
+            "time": "2020-08-25T16:54:44+00:00"
         },
         {
             "name": "maximebf/debugbar",
-            "version": "v1.16.1",
+            "version": "v1.16.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maximebf/php-debugbar.git",
-                "reference": "58998b818c6567fac01e35b8a4b70c1a64530556"
+                "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/58998b818c6567fac01e35b8a4b70c1a64530556",
-                "reference": "58998b818c6567fac01e35b8a4b70c1a64530556",
+                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/1a1605b8e9bacb34cc0c6278206d699772e1d372",
+                "reference": "1a1605b8e9bacb34cc0c6278206d699772e1d372",
                 "shasum": ""
             },
             "require": {
                 "debug",
                 "debugbar"
             ],
-            "time": "2019-11-24T09:46:11+00:00"
+            "time": "2020-05-06T07:06:27+00:00"
         },
         {
             "name": "mockery/mockery",
-            "version": "1.3.1",
+            "version": "1.3.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/mockery/mockery.git",
-                "reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be"
+                "reference": "60fa2f67f6e4d3634bb4a45ff3171fa52215800d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/mockery/mockery/zipball/f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be",
-                "reference": "f69bbde7d7a75d6b2862d9ca8fab1cd28014b4be",
+                "url": "https://api.github.com/repos/mockery/mockery/zipball/60fa2f67f6e4d3634bb4a45ff3171fa52215800d",
+                "reference": "60fa2f67f6e4d3634bb4a45ff3171fa52215800d",
                 "shasum": ""
             },
             "require": {
-                "hamcrest/hamcrest-php": "~2.0",
+                "hamcrest/hamcrest-php": "^2.0.1",
                 "lib-pcre": ">=7.0",
                 "php": ">=5.6.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0"
+                "phpunit/phpunit": "^5.7.10|^6.5|^7.5|^8.5|^9.3"
             },
             "type": "library",
             "extra": {
                 "test double",
                 "testing"
             ],
-            "time": "2019-12-26T09:49:15+00:00"
+            "time": "2020-08-11T18:10:21+00:00"
         },
         {
             "name": "myclabs/deep-copy",
-            "version": "1.9.5",
+            "version": "1.10.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/myclabs/DeepCopy.git",
-                "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef"
+                "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/b2c28789e80a97badd14145fda39b545d83ca3ef",
-                "reference": "b2c28789e80a97badd14145fda39b545d83ca3ef",
+                "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
+                "reference": "969b211f9a51aa1f6c01d1d2aef56d3bd91598e5",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1"
+                "php": "^7.1 || ^8.0"
             },
             "replace": {
                 "myclabs/deep-copy": "self.version"
                 "object",
                 "object graph"
             ],
-            "time": "2020-01-17T21:11:47+00:00"
+            "funding": [
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-06-29T13:22:24+00:00"
         },
         {
             "name": "phar-io/manifest",
         },
         {
             "name": "phpdocumentor/reflection-common",
-            "version": "2.0.0",
+            "version": "2.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionCommon.git",
-                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a"
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/63a995caa1ca9e5590304cd845c15ad6d482a62a",
-                "reference": "63a995caa1ca9e5590304cd845c15ad6d482a62a",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b",
+                "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b",
                 "shasum": ""
             },
             "require": {
-                "php": ">=7.1"
-            },
-            "require-dev": {
-                "phpunit/phpunit": "~6"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.x-dev"
+                    "dev-2.x": "2.x-dev"
                 }
             },
             "autoload": {
                 "reflection",
                 "static analysis"
             ],
-            "time": "2018-08-07T13:53:10+00:00"
+            "time": "2020-06-27T09:03:43+00:00"
         },
         {
             "name": "phpdocumentor/reflection-docblock",
-            "version": "5.1.0",
+            "version": "5.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git",
-                "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e"
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e",
-                "reference": "cd72d394ca794d3466a3b2fc09d5a6c1dc86b47e",
+                "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/069a785b2141f5bcf49f3e353548dc1cce6df556",
+                "reference": "069a785b2141f5bcf49f3e353548dc1cce6df556",
                 "shasum": ""
             },
             "require": {
-                "ext-filter": "^7.1",
-                "php": "^7.2",
-                "phpdocumentor/reflection-common": "^2.0",
-                "phpdocumentor/type-resolver": "^1.0",
-                "webmozart/assert": "^1"
+                "ext-filter": "*",
+                "php": "^7.2 || ^8.0",
+                "phpdocumentor/reflection-common": "^2.2",
+                "phpdocumentor/type-resolver": "^1.3",
+                "webmozart/assert": "^1.9.1"
             },
             "require-dev": {
-                "doctrine/instantiator": "^1",
-                "mockery/mockery": "^1"
+                "mockery/mockery": "~1.3.2"
             },
             "type": "library",
             "extra": {
                 }
             ],
             "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.",
-            "time": "2020-02-22T12:28:44+00:00"
+            "time": "2020-09-03T19:13:55+00:00"
         },
         {
             "name": "phpdocumentor/type-resolver",
-            "version": "1.1.0",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpDocumentor/TypeResolver.git",
-                "reference": "7462d5f123dfc080dfdf26897032a6513644fc95"
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/7462d5f123dfc080dfdf26897032a6513644fc95",
-                "reference": "7462d5f123dfc080dfdf26897032a6513644fc95",
+                "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
+                "reference": "6a467b8989322d92aa1c8bf2bebcc6e5c2ba55c0",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.2",
+                "php": "^7.2 || ^8.0",
                 "phpdocumentor/reflection-common": "^2.0"
             },
             "require-dev": {
-                "ext-tokenizer": "^7.2",
-                "mockery/mockery": "~1"
+                "ext-tokenizer": "*"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.x-dev"
+                    "dev-1.x": "1.x-dev"
                 }
             },
             "autoload": {
                 }
             ],
             "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names",
-            "time": "2020-02-18T18:59:58+00:00"
+            "time": "2020-09-17T18:55:26+00:00"
         },
         {
             "name": "phploc/phploc",
         },
         {
             "name": "phpspec/prophecy",
-            "version": "v1.10.3",
+            "version": "1.11.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "451c3cd1418cf640de218914901e51b064abb093"
+                "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093",
-                "reference": "451c3cd1418cf640de218914901e51b064abb093",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/b20034be5efcdab4fb60ca3a29cba2949aead160",
+                "reference": "b20034be5efcdab4fb60ca3a29cba2949aead160",
                 "shasum": ""
             },
             "require": {
-                "doctrine/instantiator": "^1.0.2",
-                "php": "^5.3|^7.0",
-                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
-                "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0",
-                "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0"
+                "doctrine/instantiator": "^1.2",
+                "php": "^7.2",
+                "phpdocumentor/reflection-docblock": "^5.0",
+                "sebastian/comparator": "^3.0 || ^4.0",
+                "sebastian/recursion-context": "^3.0 || ^4.0"
             },
             "require-dev": {
-                "phpspec/phpspec": "^2.5 || ^3.2",
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1"
+                "phpspec/phpspec": "^6.0",
+                "phpunit/phpunit": "^8.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.10.x-dev"
+                    "dev-master": "1.11.x-dev"
                 }
             },
             "autoload": {
                 "spy",
                 "stub"
             ],
-            "time": "2020-03-05T15:02:03+00:00"
+            "time": "2020-07-08T12:44:21+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
         },
         {
             "name": "phpunit/phpunit",
-            "version": "8.5.3",
+            "version": "8.5.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "67750516bc02f300e2742fed2f50177f8f37bedf"
+                "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/67750516bc02f300e2742fed2f50177f8f37bedf",
-                "reference": "67750516bc02f300e2742fed2f50177f8f37bedf",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/34c18baa6a44f1d1fbf0338907139e9dce95b997",
+                "reference": "34c18baa6a44f1d1fbf0338907139e9dce95b997",
                 "shasum": ""
             },
             "require": {
                 "testing",
                 "xunit"
             ],
-            "time": "2020-03-31T08:52:04+00:00"
+            "funding": [
+                {
+                    "url": "https://phpunit.de/donate.html",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/sebastianbergmann",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-06-22T07:06:58+00:00"
         },
         {
             "name": "sebastian/code-unit-reverse-lookup",
         },
         {
             "name": "seld/jsonlint",
-            "version": "1.7.2",
+            "version": "1.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/jsonlint.git",
-                "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19"
+                "reference": "590cfec960b77fd55e39b7d9246659e95dd6d337"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19",
-                "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19",
+                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/590cfec960b77fd55e39b7d9246659e95dd6d337",
+                "reference": "590cfec960b77fd55e39b7d9246659e95dd6d337",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3 || ^7.0"
+                "php": "^5.3 || ^7.0 || ^8.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
                 "parser",
                 "validator"
             ],
-            "time": "2019-10-24T14:27:39+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/Seldaek",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-25T06:56:57+00:00"
         },
         {
             "name": "seld/phar-utils",
-            "version": "1.1.0",
+            "version": "1.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/phar-utils.git",
-                "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0"
+                "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8800503d56b9867d43d9c303b9cbcc26016e82f0",
-                "reference": "8800503d56b9867d43d9c303b9cbcc26016e82f0",
+                "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/8674b1d84ffb47cc59a101f5d5a3b61e87d23796",
+                "reference": "8674b1d84ffb47cc59a101f5d5a3b61e87d23796",
                 "shasum": ""
             },
             "require": {
             "keywords": [
                 "phar"
             ],
-            "time": "2020-02-14T15:25:33+00:00"
+            "time": "2020-07-07T18:42:57+00:00"
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.5.4",
+            "version": "3.5.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "dceec07328401de6211037abbb18bda423677e26"
+                "reference": "e97627871a7eab2f70e59166072a6b767d5834e0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/dceec07328401de6211037abbb18bda423677e26",
-                "reference": "dceec07328401de6211037abbb18bda423677e26",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/e97627871a7eab2f70e59166072a6b767d5834e0",
+                "reference": "e97627871a7eab2f70e59166072a6b767d5834e0",
                 "shasum": ""
             },
             "require": {
                 "phpcs",
                 "standards"
             ],
-            "time": "2020-01-30T22:20:29+00:00"
+            "time": "2020-08-10T04:50:15+00:00"
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "4d0fb3374324071ecdd94898367a3fa4b5563162"
+                "reference": "6dd1e7adef4b7efeeb9691fd619279027d4dcf85"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4d0fb3374324071ecdd94898367a3fa4b5563162",
-                "reference": "4d0fb3374324071ecdd94898367a3fa4b5563162",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/6dd1e7adef4b7efeeb9691fd619279027d4dcf85",
+                "reference": "6dd1e7adef4b7efeeb9691fd619279027d4dcf85",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-ctype": "~1.8",
                 "symfony/polyfill-mbstring": "~1.0"
             },
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-29T19:12:22+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-12T06:20:35+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v4.4.7",
+            "version": "v4.4.13",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "fe297193bf2e6866ed900ed2d5869362768df6a7"
+                "reference": "27575bcbc68db1f6d06218891296572c9b845704"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/fe297193bf2e6866ed900ed2d5869362768df6a7",
-                "reference": "fe297193bf2e6866ed900ed2d5869362768df6a7",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/27575bcbc68db1f6d06218891296572c9b845704",
+                "reference": "27575bcbc68db1f6d06218891296572c9b845704",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1.3",
+                "php": ">=7.1.3",
                 "symfony/polyfill-ctype": "~1.8"
             },
             "type": "library",
             ],
             "description": "Symfony Filesystem Component",
             "homepage": "https://symfony.com",
-            "time": "2020-03-27T16:54:36+00:00"
+            "funding": [
+                {
+                    "url": "https://symfony.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/fabpot",
+                    "type": "github"
+                },
+                {
+                    "url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
+                    "type": "tidelift"
+                }
+            ],
+            "time": "2020-08-21T17:19:37+00:00"
         },
         {
             "name": "theseer/fdomdocument",
         },
         {
             "name": "theseer/tokenizer",
-            "version": "1.1.3",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/theseer/tokenizer.git",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9"
+                "reference": "75a63c33a8577608444246075ea0af0d052e452a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
-                "reference": "11336f6f84e16a720dae9d8e6ed5019efa85a0f9",
+                "url": "https://api.github.com/repos/theseer/tokenizer/zipball/75a63c33a8577608444246075ea0af0d052e452a",
+                "reference": "75a63c33a8577608444246075ea0af0d052e452a",
                 "shasum": ""
             },
             "require": {
                 "ext-dom": "*",
                 "ext-tokenizer": "*",
                 "ext-xmlwriter": "*",
-                "php": "^7.0"
+                "php": "^7.2 || ^8.0"
             },
             "type": "library",
             "autoload": {
             "authors": [
                 {
                     "name": "Arne Blankerts",
-                    "role": "Developer",
-                    "email": "[email protected]"
+                    "email": "[email protected]",
+                    "role": "Developer"
                 }
             ],
             "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
-            "time": "2019-06-13T22:48:21+00:00"
+            "funding": [
+                {
+                    "url": "https://github.com/theseer",
+                    "type": "github"
+                }
+            ],
+            "time": "2020-07-12T23:59:07+00:00"
         },
         {
             "name": "webmozart/assert",
-            "version": "1.7.0",
+            "version": "1.9.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/webmozart/assert.git",
-                "reference": "aed98a490f9a8f78468232db345ab9cf606cf598"
+                "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598",
-                "reference": "aed98a490f9a8f78468232db345ab9cf606cf598",
+                "url": "https://api.github.com/repos/webmozart/assert/zipball/bafc69caeb4d49c39fd0779086c03a3738cbb389",
+                "reference": "bafc69caeb4d49c39fd0779086c03a3738cbb389",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.3 || ^7.0",
+                "php": "^5.3.3 || ^7.0 || ^8.0",
                 "symfony/polyfill-ctype": "^1.8"
             },
             "conflict": {
-                "vimeo/psalm": "<3.6.0"
+                "phpstan/phpstan": "<0.12.20",
+                "vimeo/psalm": "<3.9.1"
             },
             "require-dev": {
                 "phpunit/phpunit": "^4.8.36 || ^7.5.13"
                 "check",
                 "validate"
             ],
-            "time": "2020-02-14T12:15:55+00:00"
+            "time": "2020-07-08T17:02:28+00:00"
         },
         {
             "name": "wnx/laravel-stats",
diff --git a/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php b/database/migrations/2020_08_04_111754_drop_joint_permissions_id.php
new file mode 100644 (file)
index 0000000..bb953a5
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class DropJointPermissionsId extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->dropColumn('id');
+            $table->primary(['role_id', 'entity_type', 'entity_id', 'action'], 'joint_primary');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->dropPrimary(['role_id', 'entity_type', 'entity_id', 'action']);
+        });
+
+        Schema::table('joint_permissions', function (Blueprint $table) {
+            $table->increments('id')->unsigned();
+        });
+    }
+}
diff --git a/database/migrations/2020_08_04_131052_remove_role_name_field.php b/database/migrations/2020_08_04_131052_remove_role_name_field.php
new file mode 100644 (file)
index 0000000..f3cafb7
--- /dev/null
@@ -0,0 +1,37 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+use Illuminate\Support\Facades\DB;
+
+class RemoveRoleNameField extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('roles', function (Blueprint $table) {
+            $table->dropColumn('name');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('roles', function (Blueprint $table) {
+            $table->string('name')->index();
+        });
+
+        DB::table('roles')->update([
+            "name" => DB::raw("lower(replace(`display_name`, ' ', '-'))"),
+        ]);
+    }
+}
diff --git a/database/migrations/2020_09_19_094251_add_activity_indexes.php b/database/migrations/2020_09_19_094251_add_activity_indexes.php
new file mode 100644 (file)
index 0000000..544b01e
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class AddActivityIndexes extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::table('activities', function(Blueprint $table) {
+            $table->index('key');
+            $table->index('created_at');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::table('activities', function(Blueprint $table) {
+            $table->dropIndex('key');
+            $table->dropIndex('created_at');
+        });
+    }
+}
diff --git a/dev/api/requests/chapters-create.json b/dev/api/requests/chapters-create.json
new file mode 100644 (file)
index 0000000..ca06fc2
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "book_id": 1,
+  "name": "My fantastic new chapter",
+  "description": "This is a great new chapter that I've created via the API",
+  "tags": [
+    {"name": "Category", "value": "Top Content"},
+    {"name": "Rating", "value": "Highest"}
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/requests/chapters-update.json b/dev/api/requests/chapters-update.json
new file mode 100644 (file)
index 0000000..6bd3a3e
--- /dev/null
@@ -0,0 +1,9 @@
+{
+  "book_id": 1,
+  "name": "My fantastic updated chapter",
+  "description": "This is an updated chapter that I've altered via the API",
+  "tags": [
+    {"name": "Category", "value": "Kinda Good Content"},
+    {"name": "Rating", "value": "Medium"}
+  ]
+}
\ No newline at end of file
index 11408e9ab2df3251081a9cffaf8adf35a16b6c6d..2e43f5f87fc810163bc8323f53e304c6cbb070db 100644 (file)
@@ -7,15 +7,12 @@
   "updated_at": "2020-01-12 14:11:51",
   "created_by": {
     "id": 1,
-    "name": "Admin",
-    "image_id": 48
+    "name": "Admin"
   },
   "updated_by": {
     "id": 1,
-    "name": "Admin",
-    "image_id": 48
+    "name": "Admin"
   },
-  "image_id": 452,
   "tags": [
     {
       "id": 13,
diff --git a/dev/api/responses/chapters-create.json b/dev/api/responses/chapters-create.json
new file mode 100644 (file)
index 0000000..7aac276
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "book_id": 1,
+  "priority": 6,
+  "name": "My fantastic new chapter",
+  "description": "This is a great new chapter that I've created via the API",
+  "created_by": 1,
+  "updated_by": 1,
+  "slug": "my-fantastic-new-chapter",
+  "updated_at": "2020-05-22 22:59:55",
+  "created_at": "2020-05-22 22:59:55",
+  "id": 74,
+  "book": {
+    "id": 1,
+    "name": "BookStack User Guide",
+    "slug": "bookstack-user-guide",
+    "description": "This is a general guide on using BookStack on a day-to-day basis.",
+    "created_at": "2019-05-05 21:48:46",
+    "updated_at": "2019-12-11 20:57:31",
+    "created_by": 1,
+    "updated_by": 1
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Top Content",
+      "order": 0,
+      "created_at": "2020-05-22 22:59:55",
+      "updated_at": "2020-05-22 22:59:55"
+    },
+    {
+      "name": "Rating",
+      "value": "Highest",
+      "order": 0,
+      "created_at": "2020-05-22 22:59:55",
+      "updated_at": "2020-05-22 22:59:55"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-list.json b/dev/api/responses/chapters-list.json
new file mode 100644 (file)
index 0000000..0c1fc5f
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "data": [
+    {
+      "id": 1,
+      "book_id": 1,
+      "name": "Content Creation",
+      "slug": "content-creation",
+      "description": "How to create documentation on whatever subject you need to write about.",
+      "priority": 3,
+      "created_at": "2019-05-05 21:49:56",
+      "updated_at": "2019-09-28 11:24:23",
+      "created_by": 1,
+      "updated_by": 1
+    },
+    {
+      "id": 2,
+      "book_id": 1,
+      "name": "Managing Content",
+      "slug": "managing-content",
+      "description": "How to keep things organised and orderly in the system for easier navigation and better user experience.",
+      "priority": 5,
+      "created_at": "2019-05-05 21:58:07",
+      "updated_at": "2019-10-17 15:05:34",
+      "created_by": 3,
+      "updated_by": 3
+    }
+  ],
+  "total": 40
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-read.json b/dev/api/responses/chapters-read.json
new file mode 100644 (file)
index 0000000..2eddad8
--- /dev/null
@@ -0,0 +1,59 @@
+{
+  "id": 1,
+  "book_id": 1,
+  "slug": "content-creation",
+  "name": "Content Creation",
+  "description": "How to create documentation on whatever subject you need to write about.",
+  "priority": 3,
+  "created_at": "2019-05-05 21:49:56",
+  "updated_at": "2019-09-28 11:24:23",
+  "created_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "updated_by": {
+    "id": 1,
+    "name": "Admin"
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Guide",
+      "order": 0,
+      "created_at": "2020-05-22 22:51:51",
+      "updated_at": "2020-05-22 22:51:51"
+    }
+  ],
+  "pages": [
+    {
+      "id": 1,
+      "book_id": 1,
+      "chapter_id": 1,
+      "name": "How to create page content",
+      "slug": "how-to-create-page-content",
+      "priority": 0,
+      "created_at": "2019-05-05 21:49:58",
+      "updated_at": "2019-08-26 14:32:59",
+      "created_by": 1,
+      "updated_by": 1,
+      "draft": 0,
+      "revision_count": 2,
+      "template": 0
+    },
+    {
+      "id": 7,
+      "book_id": 1,
+      "chapter_id": 1,
+      "name": "Good book structure",
+      "slug": "good-book-structure",
+      "priority": 1,
+      "created_at": "2019-05-05 22:01:55",
+      "updated_at": "2019-06-06 12:03:04",
+      "created_by": 3,
+      "updated_by": 3,
+      "draft": 0,
+      "revision_count": 1,
+      "template": 0
+    }
+  ]
+}
\ No newline at end of file
diff --git a/dev/api/responses/chapters-update.json b/dev/api/responses/chapters-update.json
new file mode 100644 (file)
index 0000000..a7edb15
--- /dev/null
@@ -0,0 +1,38 @@
+{
+  "id": 75,
+  "book_id": 1,
+  "slug": "my-fantastic-updated-chapter",
+  "name": "My fantastic updated chapter",
+  "description": "This is an updated chapter that I've altered via the API",
+  "priority": 7,
+  "created_at": "2020-05-22 23:03:35",
+  "updated_at": "2020-05-22 23:07:20",
+  "created_by": 1,
+  "updated_by": 1,
+  "book": {
+    "id": 1,
+    "name": "BookStack User Guide",
+    "slug": "bookstack-user-guide",
+    "description": "This is a general guide on using BookStack on a day-to-day basis.",
+    "created_at": "2019-05-05 21:48:46",
+    "updated_at": "2019-12-11 20:57:31",
+    "created_by": 1,
+    "updated_by": 1
+  },
+  "tags": [
+    {
+      "name": "Category",
+      "value": "Kinda Good Content",
+      "order": 0,
+      "created_at": "2020-05-22 23:07:20",
+      "updated_at": "2020-05-22 23:07:20"
+    },
+    {
+      "name": "Rating",
+      "value": "Medium",
+      "order": 0,
+      "created_at": "2020-05-22 23:07:20",
+      "updated_at": "2020-05-22 23:07:20"
+    }
+  ]
+}
\ No newline at end of file
index 8a8e2348b2f78df5f5eb48b78544867b35f657a6..634fbb5a53c6fde235e72c2516b111a73f645451 100644 (file)
@@ -5,15 +5,12 @@
   "description": "This is my shelf with some books",
   "created_by": {
     "id": 1,
-    "name": "Admin",
-    "image_id": 48
+    "name": "Admin"
   },
   "updated_by": {
     "id": 1,
-    "name": "Admin",
-    "image_id": 48
+    "name": "Admin"
   },
-  "image_id": 501,
   "created_at": "2020-04-10 13:24:09",
   "updated_at": "2020-04-10 13:31:04",
   "tags": [
diff --git a/dev/docs/components.md b/dev/docs/components.md
new file mode 100644 (file)
index 0000000..832765d
--- /dev/null
@@ -0,0 +1,99 @@
+# JavaScript Components
+
+This document details the format for JavaScript components in BookStack. This is a really simple class-based setup with a few helpers provided.
+
+#### Defining a Component in JS
+
+```js
+class Dropdown {
+    setup() {
+        this.toggle = this.$refs.toggle;
+        this.menu = this.$refs.menu;
+    
+        this.speed = parseInt(this.$opts.speed);
+    }
+}
+```
+
+All usage of $refs, $manyRefs and $opts should be done at the top of the `setup` function so any requirements can be easily seen.
+
+#### Using a Component in HTML
+
+A component is used like so:
+
+```html
+<div component="dropdown"></div>
+
+<!-- or, for multiple -->
+
+<div components="dropdown image-picker"></div>
+```
+
+The names will be parsed and new component instance will be created if a matching name is found in the `components/index.js` componentMapping. 
+
+#### Element References
+
+Within a component you'll often need to refer to other element instances. This can be done like so:
+
+```html
+<div component="dropdown">
+    <span refs="dropdown@toggle othercomponent@handle">View more</span>
+</div>
+```
+
+You can then access the span element as `this.$refs.toggle` in your component.
+
+#### Component Options
+
+```html
+<div component="dropdown"
+    option:dropdown:delay="500"
+    option:dropdown:show>
+</div>
+```
+
+Will result with `this.$opts` being:
+
+```json
+{
+    "delay": "500",
+    "show": ""  
+}
+```
+
+#### Global Helpers
+
+There are various global helper libraries which can be used in components:
+
+```js
+// HTTP service
+window.$http.get(url, params);
+window.$http.post(url, data);
+window.$http.put(url, data);
+window.$http.delete(url, data);
+window.$http.patch(url, data);
+
+// Global event system
+// Emit a global event
+window.$events.emit(eventName, eventData);
+// Listen to a global event
+window.$events.listen(eventName, callback);
+// Show a success message
+window.$events.success(message);
+// Show an error message
+window.$events.error(message);
+// Show validation errors, if existing, as an error notification
+window.$events.showValidationErrors(error);
+
+// Translator
+// Take the given plural text and count to decide on what plural option
+// to use, Similar to laravel's trans_choice function but instead
+// takes the direction directly instead of a translation key.
+window.trans_plural(translationString, count, replacements);
+
+// Component System
+// Parse and initialise any components from the given root el down.
+window.components.init(rootEl);
+// Get the first active component of the given name
+window.components.first(name);
+```
\ No newline at end of file
index 243a19a9b71d71a084d8f59f08deea5fc3590a7d..c5b9fc3933262ed7c357927c14264861d87d87d8 100644 (file)
   "requires": true,
   "lockfileVersion": 1,
   "dependencies": {
-    "@webassemblyjs/ast": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz",
-      "integrity": "sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/helper-module-context": "1.9.0",
-        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-        "@webassemblyjs/wast-parser": "1.9.0"
-      }
-    },
-    "@webassemblyjs/floating-point-hex-parser": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz",
-      "integrity": "sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-api-error": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz",
-      "integrity": "sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-buffer": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz",
-      "integrity": "sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-code-frame": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz",
-      "integrity": "sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/wast-printer": "1.9.0"
-      }
-    },
-    "@webassemblyjs/helper-fsm": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz",
-      "integrity": "sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-module-context": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz",
-      "integrity": "sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0"
-      }
-    },
-    "@webassemblyjs/helper-wasm-bytecode": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz",
-      "integrity": "sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==",
-      "dev": true
-    },
-    "@webassemblyjs/helper-wasm-section": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz",
-      "integrity": "sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-buffer": "1.9.0",
-        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-        "@webassemblyjs/wasm-gen": "1.9.0"
-      }
-    },
-    "@webassemblyjs/ieee754": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz",
-      "integrity": "sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==",
-      "dev": true,
-      "requires": {
-        "@xtuc/ieee754": "^1.2.0"
-      }
-    },
-    "@webassemblyjs/leb128": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.9.0.tgz",
-      "integrity": "sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==",
-      "dev": true,
-      "requires": {
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "@webassemblyjs/utf8": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.9.0.tgz",
-      "integrity": "sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==",
-      "dev": true
-    },
-    "@webassemblyjs/wasm-edit": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz",
-      "integrity": "sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-buffer": "1.9.0",
-        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-        "@webassemblyjs/helper-wasm-section": "1.9.0",
-        "@webassemblyjs/wasm-gen": "1.9.0",
-        "@webassemblyjs/wasm-opt": "1.9.0",
-        "@webassemblyjs/wasm-parser": "1.9.0",
-        "@webassemblyjs/wast-printer": "1.9.0"
-      }
-    },
-    "@webassemblyjs/wasm-gen": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz",
-      "integrity": "sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-        "@webassemblyjs/ieee754": "1.9.0",
-        "@webassemblyjs/leb128": "1.9.0",
-        "@webassemblyjs/utf8": "1.9.0"
-      }
-    },
-    "@webassemblyjs/wasm-opt": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz",
-      "integrity": "sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-buffer": "1.9.0",
-        "@webassemblyjs/wasm-gen": "1.9.0",
-        "@webassemblyjs/wasm-parser": "1.9.0"
-      }
-    },
-    "@webassemblyjs/wasm-parser": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz",
-      "integrity": "sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-api-error": "1.9.0",
-        "@webassemblyjs/helper-wasm-bytecode": "1.9.0",
-        "@webassemblyjs/ieee754": "1.9.0",
-        "@webassemblyjs/leb128": "1.9.0",
-        "@webassemblyjs/utf8": "1.9.0"
-      }
-    },
-    "@webassemblyjs/wast-parser": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz",
-      "integrity": "sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/floating-point-hex-parser": "1.9.0",
-        "@webassemblyjs/helper-api-error": "1.9.0",
-        "@webassemblyjs/helper-code-frame": "1.9.0",
-        "@webassemblyjs/helper-fsm": "1.9.0",
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "@webassemblyjs/wast-printer": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz",
-      "integrity": "sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==",
-      "dev": true,
-      "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/wast-parser": "1.9.0",
-        "@xtuc/long": "4.2.2"
-      }
-    },
-    "@xtuc/ieee754": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
-      "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==",
-      "dev": true
-    },
-    "@xtuc/long": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz",
-      "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==",
-      "dev": true
-    },
-    "abbrev": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
-      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
-      "dev": true
-    },
-    "acorn": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
-      "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
-      "dev": true
-    },
-    "ajv": {
-      "version": "6.10.2",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
-      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
-      "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"
-      }
-    },
-    "ajv-errors": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
-      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
-      "dev": true
-    },
-    "ajv-keywords": {
-      "version": "3.4.1",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
-      "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
-      "dev": true
-    },
-    "amdefine": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz",
-      "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=",
-      "dev": true
-    },
     "ansi-regex": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
-      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+      "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
       "dev": true
     },
     "ansi-styles": {
         "picomatch": "^2.0.4"
       }
     },
-    "aproba": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
-      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==",
-      "dev": true
-    },
-    "are-we-there-yet": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
-      "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
-      "dev": true,
-      "requires": {
-        "delegates": "^1.0.0",
-        "readable-stream": "^2.0.6"
-      }
-    },
     "argparse": {
       "version": "1.0.10",
       "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
         "sprintf-js": "~1.0.2"
       }
     },
-    "arr-diff": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
-      "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
-      "dev": true
-    },
-    "arr-flatten": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
-      "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
-      "dev": true
-    },
-    "arr-union": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
-      "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
-      "dev": true
-    },
-    "array-filter": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/array-filter/-/array-filter-0.0.1.tgz",
-      "integrity": "sha1-fajPLiZijtcygDWB/SH2fKzS7uw=",
-      "dev": true
-    },
-    "array-find-index": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
-      "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E=",
-      "dev": true
-    },
-    "array-map": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/array-map/-/array-map-0.0.0.tgz",
-      "integrity": "sha1-iKK6tz0c97zVwbEYoAP2b2ZfpmI=",
-      "dev": true
-    },
-    "array-reduce": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/array-reduce/-/array-reduce-0.0.0.tgz",
-      "integrity": "sha1-FziZ0//Rx9k4PkR5Ul2+J4yrXys=",
-      "dev": true
-    },
-    "array-unique": {
-      "version": "0.3.2",
-      "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
-      "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
-      "dev": true
-    },
-    "asn1": {
-      "version": "0.2.4",
-      "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
-      "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
-      "dev": true,
-      "requires": {
-        "safer-buffer": "~2.1.0"
-      }
-    },
-    "asn1.js": {
-      "version": "4.10.1",
-      "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz",
-      "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
-      }
-    },
-    "assert": {
-      "version": "1.5.0",
-      "resolved": "https://registry.npmjs.org/assert/-/assert-1.5.0.tgz",
-      "integrity": "sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA==",
-      "dev": true,
-      "requires": {
-        "object-assign": "^4.1.1",
-        "util": "0.10.3"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz",
-          "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=",
-          "dev": true
-        },
-        "util": {
-          "version": "0.10.3",
-          "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz",
-          "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=",
-          "dev": true,
-          "requires": {
-            "inherits": "2.0.1"
-          }
-        }
-      }
-    },
-    "assert-plus": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
-      "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
-      "dev": true
-    },
-    "assign-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
-      "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
-      "dev": true
-    },
-    "async-each": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
-      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
-      "dev": true
-    },
-    "async-foreach": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/async-foreach/-/async-foreach-0.1.3.tgz",
-      "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
-      "dev": true
-    },
     "async-limiter": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
       "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
       "dev": true
     },
-    "asynckit": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
-      "dev": true
-    },
-    "atob": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
-      "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
-      "dev": true
-    },
-    "aws-sign2": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
-      "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
-      "dev": true
-    },
-    "aws4": {
-      "version": "1.9.1",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
-      "integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
-      "dev": true
-    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
       "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
       "dev": true
     },
-    "base": {
-      "version": "0.11.2",
-      "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
-      "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
-      "dev": true,
-      "requires": {
-        "cache-base": "^1.0.1",
-        "class-utils": "^0.3.5",
-        "component-emitter": "^1.2.1",
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.1",
-        "mixin-deep": "^1.2.0",
-        "pascalcase": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "base64-js": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
-      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
-      "dev": true
-    },
-    "bcrypt-pbkdf": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
-      "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
-      "dev": true,
-      "requires": {
-        "tweetnacl": "^0.14.3"
-      }
-    },
-    "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
-    },
     "binary-extensions": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz",
-      "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==",
-      "dev": true
-    },
-    "block-stream": {
-      "version": "0.0.9",
-      "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
-      "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=",
-      "dev": true,
-      "requires": {
-        "inherits": "~2.0.0"
-      }
-    },
-    "bluebird": {
-      "version": "3.7.2",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
-      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true
-    },
-    "bn.js": {
-      "version": "4.11.8",
-      "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz",
-      "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz",
+      "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==",
       "dev": true
     },
     "brace-expansion": {
-      "version": "1.1.8",
-      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
-      "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
       "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "fill-range": "^7.0.1"
       }
     },
-    "brorand": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
-      "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
-      "dev": true
-    },
-    "browserify-aes": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
-      "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==",
-      "dev": true,
-      "requires": {
-        "buffer-xor": "^1.0.3",
-        "cipher-base": "^1.0.0",
-        "create-hash": "^1.1.0",
-        "evp_bytestokey": "^1.0.3",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "browserify-cipher": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz",
-      "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==",
-      "dev": true,
-      "requires": {
-        "browserify-aes": "^1.0.4",
-        "browserify-des": "^1.0.0",
-        "evp_bytestokey": "^1.0.0"
-      }
-    },
-    "browserify-des": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz",
-      "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.1",
-        "des.js": "^1.0.0",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      }
-    },
-    "browserify-rsa": {
-      "version": "4.0.1",
-      "resolved": "http://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
-      "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "randombytes": "^2.0.1"
-      }
-    },
-    "browserify-sign": {
-      "version": "4.0.4",
-      "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
-      "integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.1",
-        "browserify-rsa": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "create-hmac": "^1.1.2",
-        "elliptic": "^6.0.0",
-        "inherits": "^2.0.1",
-        "parse-asn1": "^5.0.0"
-      }
-    },
-    "browserify-zlib": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz",
-      "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==",
-      "dev": true,
-      "requires": {
-        "pako": "~1.0.5"
-      }
-    },
-    "buffer": {
-      "version": "4.9.2",
-      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
-      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
-      "dev": true,
-      "requires": {
-        "base64-js": "^1.0.2",
-        "ieee754": "^1.1.4",
-        "isarray": "^1.0.0"
-      }
-    },
-    "buffer-from": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz",
-      "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==",
-      "dev": true
-    },
-    "buffer-xor": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
-      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
-      "dev": true
-    },
-    "builtin-modules": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
-      "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=",
-      "dev": true
-    },
-    "builtin-status-codes": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
-      "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=",
-      "dev": true
-    },
-    "cacache": {
-      "version": "12.0.4",
-      "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz",
-      "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==",
-      "dev": true,
-      "requires": {
-        "bluebird": "^3.5.5",
-        "chownr": "^1.1.1",
-        "figgy-pudding": "^3.5.1",
-        "glob": "^7.1.4",
-        "graceful-fs": "^4.1.15",
-        "infer-owner": "^1.0.3",
-        "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.3",
-        "ssri": "^6.0.1",
-        "unique-filename": "^1.1.1",
-        "y18n": "^4.0.0"
-      },
-      "dependencies": {
-        "graceful-fs": {
-          "version": "4.2.3",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
-          "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
-          "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.1.1",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
-          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
-          "dev": true
-        }
-      }
-    },
-    "cache-base": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz",
-      "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==",
-      "dev": true,
-      "requires": {
-        "collection-visit": "^1.0.0",
-        "component-emitter": "^1.2.1",
-        "get-value": "^2.0.6",
-        "has-value": "^1.0.0",
-        "isobject": "^3.0.1",
-        "set-value": "^2.0.0",
-        "to-object-path": "^0.3.0",
-        "union-value": "^1.0.0",
-        "unset-value": "^1.0.0"
-      }
-    },
     "camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
       "dev": true
     },
-    "camelcase-keys": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
-      "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
-      "dev": true,
-      "requires": {
-        "camelcase": "^2.0.0",
-        "map-obj": "^1.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
-          "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=",
-          "dev": true
-        }
-      }
-    },
-    "caseless": {
-      "version": "0.12.0",
-      "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
-      "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
-      "dev": true
-    },
     "chalk": {
-      "version": "2.4.1",
-      "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz",
-      "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==",
+      "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": "^3.2.1",
       }
     },
     "chokidar": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.1.tgz",
-      "integrity": "sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg==",
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+      "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
       "dev": true,
       "requires": {
         "anymatch": "~3.1.1",
         "is-binary-path": "~2.1.0",
         "is-glob": "~4.0.1",
         "normalize-path": "~3.0.0",
-        "readdirp": "~3.3.0"
-      }
-    },
-    "chownr": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
-      "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==",
-      "dev": true
-    },
-    "chrome-trace-event": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz",
-      "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==",
-      "dev": true,
-      "requires": {
-        "tslib": "^1.9.0"
-      }
-    },
-    "cipher-base": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz",
-      "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
+        "readdirp": "~3.4.0"
       }
     },
-    "class-utils": {
-      "version": "0.3.6",
-      "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
-      "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==",
+    "chokidar-cli": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/chokidar-cli/-/chokidar-cli-2.1.0.tgz",
+      "integrity": "sha512-6n21AVpW6ywuEPoxJcLXMA2p4T+SLjWsXKny/9yTWFz0kKxESI3eUylpeV97LylING/27T/RVTY0f2/0QaWq9Q==",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "define-property": "^0.2.5",
-        "isobject": "^3.0.0",
-        "static-extend": "^0.1.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
+        "chokidar": "^3.2.3",
+        "lodash.debounce": "^4.0.8",
+        "lodash.throttle": "^4.1.1",
+        "yargs": "^13.3.0"
       }
     },
     "clipboard": {
       }
     },
     "cliui": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
-      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wrap-ansi": "^2.0.0"
-      }
-    },
-    "clone-deep": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
-      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
       "dev": true,
       "requires": {
-        "is-plain-object": "^2.0.4",
-        "kind-of": "^6.0.2",
-        "shallow-clone": "^3.0.0"
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
       }
     },
-    "code-point-at": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
-      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
-      "dev": true
-    },
     "codemirror": {
-      "version": "5.52.2",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.52.2.tgz",
-      "integrity": "sha512-WCGCixNUck2HGvY8/ZNI1jYfxPG5cRHv0VjmWuNzbtCLz8qYA5d+je4QhSSCtCaagyeOwMi/HmmPTjBgiTm2lQ=="
-    },
-    "collection-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
-      "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=",
-      "dev": true,
-      "requires": {
-        "map-visit": "^1.0.0",
-        "object-visit": "^1.0.0"
-      }
+      "version": "5.57.0",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.57.0.tgz",
+      "integrity": "sha512-WGc6UL7Hqt+8a6ZAsj/f1ApQl3NPvHY/UQSzG6fB6l4BjExgVdhFaxd7mRTw1UCiYe/6q86zHP+kfvBQcZGvUg=="
     },
     "color-convert": {
       "version": "1.9.3",
       "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
       "dev": true
     },
-    "combined-stream": {
-      "version": "1.0.8",
-      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
-      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
-      "requires": {
-        "delayed-stream": "~1.0.0"
-      }
-    },
-    "commander": {
-      "version": "2.20.3",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
-      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
-      "dev": true
-    },
-    "commondir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
-      "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=",
-      "dev": true
-    },
-    "component-emitter": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
-      "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==",
-      "dev": true
-    },
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
       "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
       "dev": true
     },
-    "concat-stream": {
-      "version": "1.6.2",
-      "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
-      "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
-      "dev": true,
-      "requires": {
-        "buffer-from": "^1.0.0",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.2.2",
-        "typedarray": "^0.0.6"
-      }
-    },
-    "console-browserify": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
-      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
-      "dev": true
-    },
-    "console-control-strings": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
-      "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=",
-      "dev": true
-    },
-    "constants-browserify": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
-      "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=",
-      "dev": true
-    },
-    "copy-concurrently": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz",
-      "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1",
-        "fs-write-stream-atomic": "^1.0.8",
-        "iferr": "^0.1.5",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.5.4",
-        "run-queue": "^1.0.0"
-      }
-    },
-    "copy-descriptor": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz",
-      "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
-      "dev": true
-    },
-    "core-util-is": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-      "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
-      "dev": true
-    },
-    "create-ecdh": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz",
-      "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "elliptic": "^6.0.0"
-      }
-    },
-    "create-hash": {
-      "version": "1.2.0",
-      "resolved": "http://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
-      "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.1",
-        "inherits": "^2.0.1",
-        "md5.js": "^1.3.4",
-        "ripemd160": "^2.0.1",
-        "sha.js": "^2.4.0"
-      }
-    },
-    "create-hmac": {
-      "version": "1.1.7",
-      "resolved": "http://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
-      "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
-      "dev": true,
-      "requires": {
-        "cipher-base": "^1.0.3",
-        "create-hash": "^1.1.0",
-        "inherits": "^2.0.1",
-        "ripemd160": "^2.0.0",
-        "safe-buffer": "^5.0.1",
-        "sha.js": "^2.4.8"
-      }
-    },
     "cross-spawn": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-3.0.1.tgz",
-      "integrity": "sha1-ElYDfsufDF9549bvE14wdwGEuYI=",
+      "version": "6.0.5",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
+      "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
       "dev": true,
       "requires": {
-        "lru-cache": "^4.0.1",
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
         "which": "^1.2.9"
       }
     },
-    "crypto-browserify": {
-      "version": "3.12.0",
-      "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz",
-      "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==",
-      "dev": true,
-      "requires": {
-        "browserify-cipher": "^1.0.0",
-        "browserify-sign": "^4.0.0",
-        "create-ecdh": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "create-hmac": "^1.1.0",
-        "diffie-hellman": "^5.0.0",
-        "inherits": "^2.0.1",
-        "pbkdf2": "^3.0.3",
-        "public-encrypt": "^4.0.0",
-        "randombytes": "^2.0.0",
-        "randomfill": "^1.0.3"
-      }
-    },
-    "css-loader": {
-      "version": "3.4.2",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.2.tgz",
-      "integrity": "sha512-jYq4zdZT0oS0Iykt+fqnzVLRIeiPWhka+7BqPn+oSIpWJAHak5tmB/WZrJ2a21JhCeFyNnnlroSl8c+MtVndzA==",
-      "dev": true,
-      "requires": {
-        "camelcase": "^5.3.1",
-        "cssesc": "^3.0.0",
-        "icss-utils": "^4.1.1",
-        "loader-utils": "^1.2.3",
-        "normalize-path": "^3.0.0",
-        "postcss": "^7.0.23",
-        "postcss-modules-extract-imports": "^2.0.0",
-        "postcss-modules-local-by-default": "^3.0.2",
-        "postcss-modules-scope": "^2.1.1",
-        "postcss-modules-values": "^3.0.0",
-        "postcss-value-parser": "^4.0.2",
-        "schema-utils": "^2.6.0"
-      }
-    },
-    "cssesc": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
-      "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
-      "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": "1.0.1",
-      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
-      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
-      "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"
-      }
-    },
-    "debug": {
-      "version": "2.6.9",
-      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
-      "dev": true,
-      "requires": {
-        "ms": "2.0.0"
-      }
-    },
     "decamelize": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
-    "decode-uri-component": {
-      "version": "0.2.0",
-      "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
-      "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
-      "dev": true
-    },
     "define-properties": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
         "object-keys": "^1.0.12"
       }
     },
-    "define-property": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz",
-      "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==",
-      "dev": true,
-      "requires": {
-        "is-descriptor": "^1.0.2",
-        "isobject": "^3.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "delayed-stream": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
-      "dev": true
-    },
     "delegate": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
       "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
     },
-    "delegates": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
-      "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=",
-      "dev": true
-    },
-    "des.js": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
-      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0"
-      }
-    },
-    "detect-file": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
-      "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=",
-      "dev": true
-    },
-    "diffie-hellman": {
-      "version": "5.0.3",
-      "resolved": "http://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
-      "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "miller-rabin": "^4.0.0",
-        "randombytes": "^2.0.0"
-      }
-    },
-    "domain-browser": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
-      "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==",
-      "dev": true
-    },
     "dropzone": {
-      "version": "5.7.0",
-      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.7.0.tgz",
-      "integrity": "sha512-kOltiZXH5cO/72I22JjE+w6BoT6uaVLfWdFMsi1PMKFkU6BZWpqRwjnsRm0o6ANGTBuZar5Piu7m/CbKqRPiYg=="
-    },
-    "duplexify": {
-      "version": "3.7.1",
-      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
-      "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0",
-        "stream-shift": "^1.0.0"
-      }
-    },
-    "ecc-jsbn": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
-      "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
-      "dev": true,
-      "requires": {
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.1.0"
-      }
-    },
-    "elliptic": {
-      "version": "6.5.2",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
-      "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.4.0",
-        "brorand": "^1.0.1",
-        "hash.js": "^1.0.0",
-        "hmac-drbg": "^1.0.0",
-        "inherits": "^2.0.1",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.0"
-      }
+      "version": "5.7.2",
+      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.7.2.tgz",
+      "integrity": "sha512-m217bJHtf0J1IiKn4Tv6mnu1h5QvQNBnKZ39gma7hzGQhIZMxYq1vYEHs4AVd4ThFwmALys+52NAOD4zdLTG4w=="
     },
     "emoji-regex": {
       "version": "7.0.3",
       "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
       "dev": true
     },
-    "emojis-list": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
-      "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
-      "dev": true
-    },
-    "end-of-stream": {
-      "version": "1.4.4",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
-      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
-      "dev": true,
-      "requires": {
-        "once": "^1.4.0"
-      }
-    },
-    "enhanced-resolve": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
-      "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "memory-fs": "^0.5.0",
-        "tapable": "^1.0.0"
-      },
-      "dependencies": {
-        "memory-fs": {
-          "version": "0.5.0",
-          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
-          "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
-          "dev": true,
-          "requires": {
-            "errno": "^0.1.3",
-            "readable-stream": "^2.0.1"
-          }
-        }
-      }
-    },
     "entities": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
-      "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
-    },
-    "errno": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz",
-      "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==",
-      "dev": true,
-      "requires": {
-        "prr": "~1.0.1"
-      }
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
+      "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
     },
     "error-ex": {
       "version": "1.3.2",
       }
     },
     "es-abstract": {
-      "version": "1.12.0",
-      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.12.0.tgz",
-      "integrity": "sha512-C8Fx/0jFmV5IPoMOFPA9P9G5NtqW+4cOPit3MIuvR2t7Ag2K15EJTpxnHAYTzL+aYQJIESYeXZmDBfOBE1HcpA==",
+      "version": "1.17.6",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+      "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
       "dev": true,
       "requires": {
-        "es-to-primitive": "^1.1.1",
+        "es-to-primitive": "^1.2.1",
         "function-bind": "^1.1.1",
-        "has": "^1.0.1",
-        "is-callable": "^1.1.3",
-        "is-regex": "^1.0.4"
+        "has": "^1.0.3",
+        "has-symbols": "^1.0.1",
+        "is-callable": "^1.2.0",
+        "is-regex": "^1.1.0",
+        "object-inspect": "^1.7.0",
+        "object-keys": "^1.1.1",
+        "object.assign": "^4.1.0",
+        "string.prototype.trimend": "^1.0.1",
+        "string.prototype.trimstart": "^1.0.1"
       }
     },
     "es-to-primitive": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz",
-      "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==",
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
       "dev": true,
       "requires": {
         "is-callable": "^1.1.4",
         "is-symbol": "^1.0.2"
       }
     },
+    "esbuild": {
+      "version": "0.6.30",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.6.30.tgz",
+      "integrity": "sha512-ZSZY461UPzTYYC3rqy1QiMtngk2WyXf+58MgC7tC22jkI90FXNgEl0hN3ipfn/UgZYzTW2GBcHiO7t0rSbHT7g==",
+      "dev": true
+    },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
-    "eslint-scope": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-      "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
       "dev": true,
       "requires": {
-        "esrecurse": "^4.1.0",
-        "estraverse": "^4.1.1"
+        "to-regex-range": "^5.0.1"
       }
     },
-    "esrecurse": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
-      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
+    "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": {
-        "estraverse": "^4.1.0"
+        "locate-path": "^3.0.0"
       }
     },
-    "estraverse": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
-      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
-      "dev": true
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
     },
-    "events": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
-      "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
-    "evp_bytestokey": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz",
-      "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==",
-      "dev": true,
-      "requires": {
-        "md5.js": "^1.3.4",
-        "safe-buffer": "^5.1.1"
-      }
-    },
-    "execa": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
-      "dev": true,
-      "requires": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        }
-      }
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
     },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
       "dev": true,
       "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
+        "is-glob": "^4.0.1"
       }
     },
-    "expand-tilde": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
-      "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=",
-      "dev": true,
+    "good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
       "requires": {
-        "homedir-polyfill": "^1.0.1"
+        "delegate": "^3.1.2"
       }
     },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+    "graceful-fs": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+      "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
       "dev": true
     },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
-      "dev": true,
-      "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "extglob": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
       "dev": true,
       "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
+        "function-bind": "^1.1.1"
       }
     },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+    "has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
       "dev": true
     },
-    "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=",
+    "has-symbols": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+      "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
       "dev": true
     },
-    "fast-json-stable-stringify": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
-      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+    "hosted-git-info": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
+      "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
       "dev": true
     },
-    "figgy-pudding": {
-      "version": "3.5.2",
-      "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz",
-      "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==",
+    "is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
       "dev": true
     },
-    "fill-range": {
-      "version": "7.0.1",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
-      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
-      "dev": true,
-      "requires": {
-        "to-regex-range": "^5.0.1"
-      }
-    },
-    "find-cache-dir": {
+    "is-binary-path": {
       "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
-      "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==",
-      "dev": true,
-      "requires": {
-        "commondir": "^1.0.1",
-        "make-dir": "^2.0.0",
-        "pkg-dir": "^3.0.0"
-      }
-    },
-    "find-up": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-      "dev": true,
-      "requires": {
-        "path-exists": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      }
-    },
-    "findup-sync": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
-      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
       "dev": true,
       "requires": {
-        "detect-file": "^1.0.0",
-        "is-glob": "^4.0.0",
-        "micromatch": "^3.0.4",
-        "resolve-dir": "^1.0.1"
+        "binary-extensions": "^2.0.0"
       }
     },
-    "flush-write-stream": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz",
-      "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.3.6"
-      }
+    "is-callable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
+      "dev": true
     },
-    "for-in": {
+    "is-date-object": {
       "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "dev": true
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
       "dev": true
     },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+    "is-fullwidth-code-point": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
+      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
       "dev": true
     },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+    "is-glob": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
       "dev": true,
       "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
+        "is-extglob": "^2.1.1"
       }
     },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-regex": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
+      "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
       "dev": true,
       "requires": {
-        "map-cache": "^0.2.2"
+        "has-symbols": "^1.0.1"
       }
     },
-    "from2": {
-      "version": "2.3.0",
-      "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
-      "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=",
+    "is-symbol": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
+      "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
       "dev": true,
       "requires": {
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.0.0"
+        "has-symbols": "^1.0.1"
       }
     },
-    "fs-write-stream-atomic": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
-      "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "iferr": "^0.1.5",
-        "imurmurhash": "^0.1.4",
-        "readable-stream": "1 || 2"
-      }
-    },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
-      "dev": true
-    },
-    "fsevents": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
-      "integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
-      "dev": true,
-      "optional": true
-    },
-    "fstream": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz",
-      "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "inherits": "~2.0.0",
-        "mkdirp": ">=0.5 0",
-        "rimraf": "2"
-      }
-    },
-    "function-bind": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
-      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
-      "dev": true
-    },
-    "gauge": {
-      "version": "2.7.4",
-      "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
-      "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.0.3",
-        "console-control-strings": "^1.0.0",
-        "has-unicode": "^2.0.0",
-        "object-assign": "^4.1.0",
-        "signal-exit": "^3.0.0",
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1",
-        "wide-align": "^1.1.0"
-      }
-    },
-    "gaze": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
-      "integrity": "sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==",
-      "dev": true,
-      "requires": {
-        "globule": "^1.0.0"
-      }
-    },
-    "get-caller-file": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
-      "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
-      "dev": true
-    },
-    "get-stdin": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
-      "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=",
-      "dev": true
-    },
-    "get-stream": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
-      "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
-      "dev": true,
-      "requires": {
-        "pump": "^3.0.0"
-      }
-    },
-    "get-value": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
-      "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=",
-      "dev": true
-    },
-    "getpass": {
-      "version": "0.1.7",
-      "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
-      "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0"
-      }
-    },
-    "glob": {
-      "version": "7.1.6",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-      "dev": true,
-      "requires": {
-        "fs.realpath": "^1.0.0",
-        "inflight": "^1.0.4",
-        "inherits": "2",
-        "minimatch": "^3.0.4",
-        "once": "^1.3.0",
-        "path-is-absolute": "^1.0.0"
-      }
-    },
-    "glob-parent": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
-      "integrity": "sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw==",
-      "dev": true,
-      "requires": {
-        "is-glob": "^4.0.1"
-      }
-    },
-    "global-modules": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
-      "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
-      "dev": true,
-      "requires": {
-        "global-prefix": "^3.0.0"
-      },
-      "dependencies": {
-        "global-prefix": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
-          "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
-          "dev": true,
-          "requires": {
-            "ini": "^1.3.5",
-            "kind-of": "^6.0.2",
-            "which": "^1.3.1"
-          }
-        }
-      }
-    },
-    "global-prefix": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
-      "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.2",
-        "homedir-polyfill": "^1.0.1",
-        "ini": "^1.3.4",
-        "is-windows": "^1.0.1",
-        "which": "^1.2.14"
-      }
-    },
-    "globule": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.1.tgz",
-      "integrity": "sha512-OVyWOHgw29yosRHCHo7NncwR1hW5ew0W/UrvtwvjefVJeQ26q4/8r8FmPsSF1hJ93IgWkyv16pCTz6WblMzm/g==",
-      "dev": true,
-      "requires": {
-        "glob": "~7.1.1",
-        "lodash": "~4.17.12",
-        "minimatch": "~3.0.2"
-      }
-    },
-    "good-listener": {
-      "version": "1.2.2",
-      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
-      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
-      "requires": {
-        "delegate": "^3.1.2"
-      }
-    },
-    "graceful-fs": {
-      "version": "4.1.11",
-      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
-      "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
-      "dev": true
-    },
-    "har-schema": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
-      "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
-      "dev": true
-    },
-    "har-validator": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.5.5",
-        "har-schema": "^2.0.0"
-      }
-    },
-    "has": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
-      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
-      "dev": true,
-      "requires": {
-        "function-bind": "^1.1.1"
-      }
-    },
-    "has-ansi": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
-      "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "has-flag": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
-      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
-      "dev": true
-    },
-    "has-symbols": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz",
-      "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=",
-      "dev": true
-    },
-    "has-unicode": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
-      "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=",
-      "dev": true
-    },
-    "has-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz",
-      "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=",
-      "dev": true,
-      "requires": {
-        "get-value": "^2.0.6",
-        "has-values": "^1.0.0",
-        "isobject": "^3.0.0"
-      }
-    },
-    "has-values": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz",
-      "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=",
-      "dev": true,
-      "requires": {
-        "is-number": "^3.0.0",
-        "kind-of": "^4.0.0"
-      },
-      "dependencies": {
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "kind-of": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz",
-          "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "hash-base": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
-      "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "hash.js": {
-      "version": "1.1.7",
-      "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
-      "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.3",
-        "minimalistic-assert": "^1.0.1"
-      }
-    },
-    "hmac-drbg": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
-      "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
-      "dev": true,
-      "requires": {
-        "hash.js": "^1.0.3",
-        "minimalistic-assert": "^1.0.0",
-        "minimalistic-crypto-utils": "^1.0.1"
-      }
-    },
-    "homedir-polyfill": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz",
-      "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==",
-      "dev": true,
-      "requires": {
-        "parse-passwd": "^1.0.0"
-      }
-    },
-    "hosted-git-info": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz",
-      "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==",
-      "dev": true
-    },
-    "http-signature": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
-      "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "^1.0.0",
-        "jsprim": "^1.2.2",
-        "sshpk": "^1.7.0"
-      }
-    },
-    "https-browserify": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
-      "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
-      "dev": true
-    },
-    "icss-utils": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
-      "integrity": "sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.14"
-      }
-    },
-    "ieee754": {
-      "version": "1.1.13",
-      "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
-      "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
-      "dev": true
-    },
-    "iferr": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
-      "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
-      "dev": true
-    },
-    "import-local": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
-      "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==",
-      "dev": true,
-      "requires": {
-        "pkg-dir": "^3.0.0",
-        "resolve-cwd": "^2.0.0"
-      }
-    },
-    "imurmurhash": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
-      "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
-      "dev": true
-    },
-    "in-publish": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz",
-      "integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=",
-      "dev": true
-    },
-    "indent-string": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
-      "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
-      "dev": true,
-      "requires": {
-        "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
-    },
-    "infer-owner": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
-      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
-      "dev": true
-    },
-    "inflight": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-      "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
-      "requires": {
-        "once": "^1.3.0",
-        "wrappy": "1"
-      }
-    },
-    "inherits": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
-    },
-    "ini": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
-      "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
-      "dev": true
-    },
-    "interpret": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
-      "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
-      "dev": true
-    },
-    "invert-kv": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
-      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=",
-      "dev": true
-    },
-    "is-accessor-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
-      "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-arrayish": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
-      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
-      "dev": true
-    },
-    "is-binary-path": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
-      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
-      "dev": true,
-      "requires": {
-        "binary-extensions": "^2.0.0"
-      }
-    },
-    "is-buffer": {
-      "version": "1.1.6",
-      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
-      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
-      "dev": true
-    },
-    "is-builtin-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
-      "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
-      "dev": true,
-      "requires": {
-        "builtin-modules": "^1.0.0"
-      }
-    },
-    "is-callable": {
-      "version": "1.1.4",
-      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz",
-      "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==",
-      "dev": true
-    },
-    "is-data-descriptor": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
-      "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "is-date-object": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz",
-      "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=",
-      "dev": true
-    },
-    "is-descriptor": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz",
-      "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==",
-      "dev": true,
-      "requires": {
-        "is-accessor-descriptor": "^0.1.6",
-        "is-data-descriptor": "^0.1.4",
-        "kind-of": "^5.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
-      }
-    },
-    "is-extendable": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
-      "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=",
-      "dev": true
-    },
-    "is-extglob": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
-      "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
-      "dev": true
-    },
-    "is-finite": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz",
-      "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==",
-      "dev": true
-    },
-    "is-fullwidth-code-point": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
-      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
-      "dev": true,
-      "requires": {
-        "number-is-nan": "^1.0.0"
-      }
-    },
-    "is-glob": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
-      "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
-      "dev": true,
-      "requires": {
-        "is-extglob": "^2.1.1"
-      }
-    },
-    "is-number": {
-      "version": "7.0.0",
-      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
-      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
-      "dev": true
-    },
-    "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": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "is-regex": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
-      "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=",
-      "dev": true,
-      "requires": {
-        "has": "^1.0.1"
-      }
-    },
-    "is-stream": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
-      "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=",
-      "dev": true
-    },
-    "is-symbol": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz",
-      "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==",
-      "dev": true,
-      "requires": {
-        "has-symbols": "^1.0.0"
-      }
-    },
-    "is-typedarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
-      "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
-      "dev": true
-    },
-    "is-utf8": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
-      "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=",
-      "dev": true
-    },
-    "is-windows": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz",
-      "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==",
-      "dev": true
-    },
-    "is-wsl": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
-      "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=",
-      "dev": true
-    },
-    "isarray": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
-      "dev": true
-    },
-    "isexe": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
-      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
-      "dev": true
-    },
-    "isobject": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
-      "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=",
-      "dev": true
-    },
-    "isstream": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
-      "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
-      "dev": true
-    },
-    "js-base64": {
-      "version": "2.5.2",
-      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.5.2.tgz",
-      "integrity": "sha512-Vg8czh0Q7sFBSUMWWArX/miJeBWYBPpdU/3M/DKSaekLMqrqVPaedp+5mZhie/r0lgrcaYBfwXatEew6gwgiQQ==",
-      "dev": true
-    },
-    "jsbn": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
-      "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
-      "dev": true
-    },
-    "json-parse-better-errors": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
-      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
-      "dev": true
-    },
-    "json-schema": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
-      "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
-      "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
-    },
-    "json-stringify-safe": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
-      "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
-      "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": {
-        "minimist": "^1.2.0"
-      }
-    },
-    "jsonify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz",
-      "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=",
-      "dev": true
-    },
-    "jsprim": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
-      "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
-      "dev": true,
-      "requires": {
-        "assert-plus": "1.0.0",
-        "extsprintf": "1.3.0",
-        "json-schema": "0.2.3",
-        "verror": "1.10.0"
-      }
-    },
-    "kind-of": {
-      "version": "6.0.3",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
-      "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
-      "dev": true
-    },
-    "lcid": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
-      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
-      "dev": true,
-      "requires": {
-        "invert-kv": "^1.0.0"
-      }
-    },
-    "linkify-it": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
-      "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
-      "requires": {
-        "uc.micro": "^1.0.1"
-      }
-    },
-    "livereload": {
-      "version": "0.9.1",
-      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz",
-      "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==",
-      "dev": true,
-      "requires": {
-        "chokidar": "^3.3.0",
-        "livereload-js": "^3.1.0",
-        "opts": ">= 1.2.0",
-        "ws": "^6.2.1"
-      }
-    },
-    "livereload-js": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.2.2.tgz",
-      "integrity": "sha512-xhScbNeC687ZINjEf/bD+BMiPx4s4q0mehcLb3zCc8+mykOtmaBR4vqzyIV9rIGdG9JjHaT0LiFdscvivCjX1Q==",
-      "dev": true
-    },
-    "load-json-file": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
-      "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "parse-json": "^2.2.0",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0",
-        "strip-bom": "^2.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
-      }
-    },
-    "loader-runner": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz",
-      "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==",
-      "dev": true
-    },
-    "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"
-      }
-    },
-    "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"
-      },
-      "dependencies": {
-        "path-exists": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
-          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
-          "dev": true
-        }
-      }
-    },
-    "lodash": {
-      "version": "4.17.15",
-      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
-      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
-      "dev": true
-    },
-    "loud-rejection": {
-      "version": "1.6.0",
-      "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
-      "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
-      "dev": true,
-      "requires": {
-        "currently-unhandled": "^0.4.1",
-        "signal-exit": "^3.0.0"
-      }
-    },
-    "lru-cache": {
-      "version": "4.1.5",
-      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
-      "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
-      "dev": true,
-      "requires": {
-        "pseudomap": "^1.0.2",
-        "yallist": "^2.1.2"
-      }
-    },
-    "make-dir": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
-      "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==",
-      "dev": true,
-      "requires": {
-        "pify": "^4.0.1",
-        "semver": "^5.6.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "4.0.1",
-          "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz",
-          "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==",
-          "dev": true
-        }
-      }
-    },
-    "map-age-cleaner": {
-      "version": "0.1.3",
-      "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz",
-      "integrity": "sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==",
-      "dev": true,
-      "requires": {
-        "p-defer": "^1.0.0"
-      }
-    },
-    "map-cache": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz",
-      "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=",
-      "dev": true
-    },
-    "map-obj": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
-      "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=",
-      "dev": true
-    },
-    "map-visit": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz",
-      "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=",
-      "dev": true,
-      "requires": {
-        "object-visit": "^1.0.0"
-      }
-    },
-    "markdown-it": {
-      "version": "10.0.0",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
-      "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
-      "requires": {
-        "argparse": "^1.0.7",
-        "entities": "~2.0.0",
-        "linkify-it": "^2.0.0",
-        "mdurl": "^1.0.1",
-        "uc.micro": "^1.0.5"
-      }
-    },
-    "markdown-it-task-lists": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
-      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
-    },
-    "md5.js": {
-      "version": "1.3.5",
-      "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
-      "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
-      "dev": true,
-      "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      }
-    },
-    "mdurl": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
-      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
-    },
-    "mem": {
-      "version": "4.3.0",
-      "resolved": "https://registry.npmjs.org/mem/-/mem-4.3.0.tgz",
-      "integrity": "sha512-qX2bG48pTqYRVmDB37rn/6PT7LcR8T7oAX3bf99u1Tt1nzxYfxkgqDwUwolPlXweM0XzBOBFzSx4kfp7KP1s/w==",
-      "dev": true,
-      "requires": {
-        "map-age-cleaner": "^0.1.1",
-        "mimic-fn": "^2.0.0",
-        "p-is-promise": "^2.0.0"
-      }
-    },
-    "memory-fs": {
-      "version": "0.4.1",
-      "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
-      "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
-      "dev": true,
-      "requires": {
-        "errno": "^0.1.3",
-        "readable-stream": "^2.0.1"
-      }
-    },
-    "memorystream": {
-      "version": "0.3.1",
-      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
-      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
-      "dev": true
-    },
-    "meow": {
-      "version": "3.7.0",
-      "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
-      "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
-      "dev": true,
-      "requires": {
-        "camelcase-keys": "^2.0.0",
-        "decamelize": "^1.1.2",
-        "loud-rejection": "^1.0.0",
-        "map-obj": "^1.0.1",
-        "minimist": "^1.1.3",
-        "normalize-package-data": "^2.3.4",
-        "object-assign": "^4.0.1",
-        "read-pkg-up": "^1.0.1",
-        "redent": "^1.0.0",
-        "trim-newlines": "^1.0.0"
-      }
-    },
-    "micromatch": {
-      "version": "3.1.10",
-      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
-      "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "braces": "^2.3.1",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "extglob": "^2.0.4",
-        "fragment-cache": "^0.2.1",
-        "kind-of": "^6.0.2",
-        "nanomatch": "^1.2.9",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.2"
-      },
-      "dependencies": {
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          },
-          "dependencies": {
-            "extend-shallow": {
-              "version": "2.0.1",
-              "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-              "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-              "dev": true,
-              "requires": {
-                "is-extendable": "^0.1.0"
-              }
-            }
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          },
-          "dependencies": {
-            "kind-of": {
-              "version": "3.2.2",
-              "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-              "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-              "dev": true,
-              "requires": {
-                "is-buffer": "^1.1.5"
-              }
-            }
-          }
-        },
-        "to-regex-range": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-          "dev": true,
-          "requires": {
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1"
-          }
-        }
-      }
-    },
-    "miller-rabin": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz",
-      "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.0.0",
-        "brorand": "^1.0.1"
-      }
-    },
-    "mime-db": {
-      "version": "1.43.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.43.0.tgz",
-      "integrity": "sha512-+5dsGEEovYbT8UY9yD7eE4XTc4UwJ1jBYlgaQQF38ENsKR3wj/8q8RFZrF9WIZpB2V1ArTVFUva8sAul1NzRzQ==",
-      "dev": true
-    },
-    "mime-types": {
-      "version": "2.1.26",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.26.tgz",
-      "integrity": "sha512-01paPWYgLrkqAyrlDorC1uDwl2p3qZT7yl806vW7DvDoxwXi46jsjFbg+WdwotBIk6/MbEhO/dh5aZ5sNj/dWQ==",
-      "dev": true,
-      "requires": {
-        "mime-db": "1.43.0"
-      }
-    },
-    "mimic-fn": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
-      "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
-      "dev": true
-    },
-    "mini-css-extract-plugin": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
-      "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.1.0",
-        "normalize-url": "1.9.1",
-        "schema-utils": "^1.0.0",
-        "webpack-sources": "^1.1.0"
-      },
-      "dependencies": {
-        "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",
-      "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==",
-      "dev": true
-    },
-    "minimalistic-crypto-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
-      "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=",
-      "dev": true
-    },
-    "minimatch": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-      "dev": true,
-      "requires": {
-        "brace-expansion": "^1.1.7"
-      }
-    },
-    "minimist": {
-      "version": "1.2.5",
-      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-      "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-      "dev": true
-    },
-    "mississippi": {
-      "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",
-        "duplexify": "^3.4.2",
-        "end-of-stream": "^1.1.0",
-        "flush-write-stream": "^1.0.0",
-        "from2": "^2.1.0",
-        "parallel-transform": "^1.1.0",
-        "pump": "^3.0.0",
-        "pumpify": "^1.3.3",
-        "stream-each": "^1.1.0",
-        "through2": "^2.0.0"
-      }
-    },
-    "mixin-deep": {
-      "version": "1.3.2",
-      "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
-      "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.2",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
-      }
-    },
-    "mkdirp": {
-      "version": "0.5.5",
-      "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-      "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-      "dev": true,
-      "requires": {
-        "minimist": "^1.2.5"
-      }
-    },
-    "move-concurrently": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
-      "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1",
-        "copy-concurrently": "^1.0.0",
-        "fs-write-stream-atomic": "^1.0.8",
-        "mkdirp": "^0.5.1",
-        "rimraf": "^2.5.4",
-        "run-queue": "^1.0.3"
-      }
-    },
-    "ms": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
-      "dev": true
-    },
-    "nan": {
-      "version": "2.14.0",
-      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
-      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==",
-      "dev": true
-    },
-    "nanomatch": {
-      "version": "1.2.13",
-      "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
-      "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==",
-      "dev": true,
-      "requires": {
-        "arr-diff": "^4.0.0",
-        "array-unique": "^0.3.2",
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "fragment-cache": "^0.2.1",
-        "is-windows": "^1.0.2",
-        "kind-of": "^6.0.2",
-        "object.pick": "^1.3.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
-      }
-    },
-    "neo-async": {
-      "version": "2.6.1",
-      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
-      "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
-      "dev": true
-    },
-    "nice-try": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
-      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
-      "dev": true
-    },
-    "node-gyp": {
-      "version": "3.8.0",
-      "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz",
-      "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==",
-      "dev": true,
-      "requires": {
-        "fstream": "^1.0.0",
-        "glob": "^7.0.3",
-        "graceful-fs": "^4.1.2",
-        "mkdirp": "^0.5.0",
-        "nopt": "2 || 3",
-        "npmlog": "0 || 1 || 2 || 3 || 4",
-        "osenv": "0",
-        "request": "^2.87.0",
-        "rimraf": "2",
-        "semver": "~5.3.0",
-        "tar": "^2.0.0",
-        "which": "1"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "5.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz",
-          "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=",
-          "dev": true
-        }
-      }
-    },
-    "node-libs-browser": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
-      "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
-      "dev": true,
-      "requires": {
-        "assert": "^1.1.1",
-        "browserify-zlib": "^0.2.0",
-        "buffer": "^4.3.0",
-        "console-browserify": "^1.1.0",
-        "constants-browserify": "^1.0.0",
-        "crypto-browserify": "^3.11.0",
-        "domain-browser": "^1.1.1",
-        "events": "^3.0.0",
-        "https-browserify": "^1.0.0",
-        "os-browserify": "^0.3.0",
-        "path-browserify": "0.0.1",
-        "process": "^0.11.10",
-        "punycode": "^1.2.4",
-        "querystring-es3": "^0.2.0",
-        "readable-stream": "^2.3.3",
-        "stream-browserify": "^2.0.1",
-        "stream-http": "^2.7.2",
-        "string_decoder": "^1.0.0",
-        "timers-browserify": "^2.0.4",
-        "tty-browserify": "0.0.0",
-        "url": "^0.11.0",
-        "util": "^0.11.0",
-        "vm-browserify": "^1.0.1"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.4.1",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
-          "dev": true
-        }
-      }
-    },
-    "node-sass": {
-      "version": "4.13.1",
-      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.1.tgz",
-      "integrity": "sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw==",
-      "dev": true,
-      "requires": {
-        "async-foreach": "^0.1.3",
-        "chalk": "^1.1.1",
-        "cross-spawn": "^3.0.0",
-        "gaze": "^1.0.0",
-        "get-stdin": "^4.0.1",
-        "glob": "^7.0.3",
-        "in-publish": "^2.0.0",
-        "lodash": "^4.17.15",
-        "meow": "^3.7.0",
-        "mkdirp": "^0.5.1",
-        "nan": "^2.13.2",
-        "node-gyp": "^3.8.0",
-        "npmlog": "^4.0.0",
-        "request": "^2.88.0",
-        "sass-graph": "^2.2.4",
-        "stdout-stream": "^1.4.0",
-        "true-case-path": "^1.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"
-          }
-        },
-        "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
-        }
-      }
-    },
-    "nopt": {
-      "version": "3.0.6",
-      "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
-      "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=",
-      "dev": true,
-      "requires": {
-        "abbrev": "1"
-      }
-    },
-    "normalize-package-data": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
-      "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
-      "dev": true,
-      "requires": {
-        "hosted-git-info": "^2.1.4",
-        "is-builtin-module": "^1.0.0",
-        "semver": "2 || 3 || 4 || 5",
-        "validate-npm-package-license": "^3.0.1"
-      }
-    },
-    "normalize-path": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
-      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
-      "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",
-      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
-      "dev": true,
-      "requires": {
-        "ansi-styles": "^3.2.1",
-        "chalk": "^2.4.1",
-        "cross-spawn": "^6.0.5",
-        "memorystream": "^0.3.1",
-        "minimatch": "^3.0.4",
-        "pidtree": "^0.3.0",
-        "read-pkg": "^3.0.0",
-        "shell-quote": "^1.6.1",
-        "string.prototype.padend": "^3.0.0"
-      },
-      "dependencies": {
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "load-json-file": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
-          "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "parse-json": "^4.0.0",
-            "pify": "^3.0.0",
-            "strip-bom": "^3.0.0"
-          }
-        },
-        "parse-json": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
-          "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
-          "dev": true,
-          "requires": {
-            "error-ex": "^1.3.1",
-            "json-parse-better-errors": "^1.0.1"
-          }
-        },
-        "path-type": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
-          "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
-          "dev": true,
-          "requires": {
-            "pify": "^3.0.0"
-          }
-        },
-        "read-pkg": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
-          "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
-          "dev": true,
-          "requires": {
-            "load-json-file": "^4.0.0",
-            "normalize-package-data": "^2.3.2",
-            "path-type": "^3.0.0"
-          }
-        },
-        "strip-bom": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
-          "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
-          "dev": true
-        }
-      }
-    },
-    "npm-run-path": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
-      "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=",
-      "dev": true,
-      "requires": {
-        "path-key": "^2.0.0"
-      }
-    },
-    "npmlog": {
-      "version": "4.1.2",
-      "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
-      "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
-      "dev": true,
-      "requires": {
-        "are-we-there-yet": "~1.1.2",
-        "console-control-strings": "~1.1.0",
-        "gauge": "~2.7.3",
-        "set-blocking": "~2.0.0"
-      }
-    },
-    "number-is-nan": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
-      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
-      "dev": true
-    },
-    "oauth-sign": {
-      "version": "0.9.0",
-      "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
-      "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
-      "dev": true
-    },
-    "object-assign": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
-      "dev": true
-    },
-    "object-copy": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz",
-      "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=",
-      "dev": true,
-      "requires": {
-        "copy-descriptor": "^0.1.0",
-        "define-property": "^0.2.5",
-        "kind-of": "^3.0.3"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "object-keys": {
-      "version": "1.0.12",
-      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz",
-      "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==",
-      "dev": true
-    },
-    "object-visit": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz",
-      "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.0"
-      }
-    },
-    "object.pick": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz",
-      "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=",
-      "dev": true,
-      "requires": {
-        "isobject": "^3.0.1"
-      }
-    },
-    "once": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-      "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-      "dev": true,
-      "requires": {
-        "wrappy": "1"
-      }
-    },
-    "opts": {
-      "version": "1.2.7",
-      "resolved": "https://registry.npmjs.org/opts/-/opts-1.2.7.tgz",
-      "integrity": "sha512-hwZhzGGG/GQ7igxAVFOEun2N4fWul31qE9nfBdCnZGQCB5+L7tN9xZ+94B4aUpLOJx/of3zZs5XsuubayQYQjA==",
-      "dev": true
-    },
-    "os-browserify": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
-      "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=",
-      "dev": true
-    },
-    "os-homedir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
-      "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
-      "dev": true
-    },
-    "os-locale": {
-      "version": "1.4.0",
-      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
-      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
-      "dev": true,
-      "requires": {
-        "lcid": "^1.0.0"
-      }
-    },
-    "os-tmpdir": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
-      "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
-      "dev": true
-    },
-    "osenv": {
-      "version": "0.1.5",
-      "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
-      "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
-      "dev": true,
-      "requires": {
-        "os-homedir": "^1.0.0",
-        "os-tmpdir": "^1.0.0"
-      }
-    },
-    "p-defer": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
-      "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=",
-      "dev": true
-    },
-    "p-finally": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
-      "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
-      "dev": true
-    },
-    "p-is-promise": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz",
-      "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==",
-      "dev": true
-    },
-    "p-limit": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
-      "integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
-      "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.2.0",
-      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
-      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
-      "dev": true
-    },
-    "pako": {
-      "version": "1.0.11",
-      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
-      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
-      "dev": true
-    },
-    "parallel-transform": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
-      "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
-      "dev": true,
-      "requires": {
-        "cyclist": "^1.0.1",
-        "inherits": "^2.0.3",
-        "readable-stream": "^2.1.5"
-      }
-    },
-    "parse-asn1": {
-      "version": "5.1.5",
-      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
-      "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
-      "dev": true,
-      "requires": {
-        "asn1.js": "^4.0.0",
-        "browserify-aes": "^1.0.0",
-        "create-hash": "^1.1.0",
-        "evp_bytestokey": "^1.0.0",
-        "pbkdf2": "^3.0.3",
-        "safe-buffer": "^5.1.1"
-      }
-    },
-    "parse-json": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
-      "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
-      "dev": true,
-      "requires": {
-        "error-ex": "^1.2.0"
-      }
-    },
-    "parse-passwd": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
-      "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=",
-      "dev": true
-    },
-    "pascalcase": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz",
-      "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=",
-      "dev": true
-    },
-    "path-browserify": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
-      "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
-      "dev": true
-    },
-    "path-dirname": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz",
-      "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=",
-      "dev": true
-    },
-    "path-exists": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-      "dev": true,
-      "requires": {
-        "pinkie-promise": "^2.0.0"
-      }
-    },
-    "path-is-absolute": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-      "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
-      "dev": true
-    },
-    "path-key": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
-      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
-      "dev": true
-    },
-    "path-type": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
-      "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
-      "dev": true,
-      "requires": {
-        "graceful-fs": "^4.1.2",
-        "pify": "^2.0.0",
-        "pinkie-promise": "^2.0.0"
-      },
-      "dependencies": {
-        "pify": {
-          "version": "2.3.0",
-          "resolved": "http://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
-          "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
-          "dev": true
-        }
-      }
-    },
-    "pbkdf2": {
-      "version": "3.0.17",
-      "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
-      "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==",
-      "dev": true,
-      "requires": {
-        "create-hash": "^1.1.2",
-        "create-hmac": "^1.1.4",
-        "ripemd160": "^2.0.1",
-        "safe-buffer": "^5.0.1",
-        "sha.js": "^2.4.8"
-      }
-    },
-    "performance-now": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
-      "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
-      "dev": true
-    },
-    "picomatch": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
-      "integrity": "sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA==",
-      "dev": true
-    },
-    "pidtree": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.0.tgz",
-      "integrity": "sha512-9CT4NFlDcosssyg8KVFltgokyKZIFjoBxw8CTGy+5F38Y1eQWrt8tRayiUOXE+zVKQnYu5BR8JjCtvK3BcnBhg==",
-      "dev": true
-    },
-    "pify": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
-      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
-      "dev": true
-    },
-    "pinkie": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
-      "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
-      "dev": true
-    },
-    "pinkie-promise": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
-      "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
-      "dev": true,
-      "requires": {
-        "pinkie": "^2.0.0"
-      }
-    },
-    "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"
-      },
-      "dependencies": {
-        "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"
-          }
-        }
-      }
-    },
-    "posix-character-classes": {
-      "version": "0.1.1",
-      "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
-      "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
-      "dev": true
-    },
-    "postcss": {
-      "version": "7.0.27",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.27.tgz",
-      "integrity": "sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.4.2",
-        "source-map": "^0.6.1",
-        "supports-color": "^6.1.0"
-      },
-      "dependencies": {
-        "chalk": {
-          "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": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "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"
-              }
-            }
-          }
-        },
-        "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": {
-            "has-flag": "^3.0.0"
-          }
-        }
-      }
-    },
-    "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": "3.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz",
-      "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^4.1.1",
-        "postcss": "^7.0.16",
-        "postcss-selector-parser": "^6.0.2",
-        "postcss-value-parser": "^4.0.0"
-      }
-    },
-    "postcss-modules-scope": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz",
-      "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==",
-      "dev": true,
-      "requires": {
-        "postcss": "^7.0.6",
-        "postcss-selector-parser": "^6.0.0"
-      }
-    },
-    "postcss-modules-values": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz",
-      "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==",
-      "dev": true,
-      "requires": {
-        "icss-utils": "^4.0.0",
-        "postcss": "^7.0.6"
-      }
-    },
-    "postcss-selector-parser": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz",
-      "integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==",
-      "dev": true,
-      "requires": {
-        "cssesc": "^3.0.0",
-        "indexes-of": "^1.0.1",
-        "uniq": "^1.0.1"
-      }
-    },
-    "postcss-value-parser": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz",
-      "integrity": "sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==",
-      "dev": true
-    },
-    "prepend-http": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz",
-      "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=",
-      "dev": true
-    },
-    "process": {
-      "version": "0.11.10",
-      "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
-      "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=",
-      "dev": true
-    },
-    "process-nextick-args": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
-      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
-      "dev": true
-    },
-    "promise-inflight": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
-      "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=",
-      "dev": true
-    },
-    "prr": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz",
-      "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
-      "dev": true
-    },
-    "pseudomap": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
-      "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=",
-      "dev": true
-    },
-    "psl": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
-      "integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==",
-      "dev": true
-    },
-    "public-encrypt": {
-      "version": "4.0.3",
-      "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz",
-      "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==",
-      "dev": true,
-      "requires": {
-        "bn.js": "^4.1.0",
-        "browserify-rsa": "^4.0.0",
-        "create-hash": "^1.1.0",
-        "parse-asn1": "^5.0.0",
-        "randombytes": "^2.0.1",
-        "safe-buffer": "^5.1.2"
-      }
-    },
-    "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"
-      }
-    },
-    "pumpify": {
-      "version": "1.5.1",
-      "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz",
-      "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==",
-      "dev": true,
-      "requires": {
-        "duplexify": "^3.6.0",
-        "inherits": "^2.0.3",
-        "pump": "^2.0.0"
-      },
-      "dependencies": {
-        "pump": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz",
-          "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==",
-          "dev": true,
-          "requires": {
-            "end-of-stream": "^1.1.0",
-            "once": "^1.3.1"
-          }
-        }
-      }
-    },
-    "punycode": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
-      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
-      "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",
-      "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
-      "dev": true
-    },
-    "querystring-es3": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz",
-      "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
-      "dev": true
-    },
-    "randombytes": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
-      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "randomfill": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz",
-      "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==",
-      "dev": true,
-      "requires": {
-        "randombytes": "^2.0.5",
-        "safe-buffer": "^5.1.0"
-      }
-    },
-    "read-pkg": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
-      "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
-      "dev": true,
-      "requires": {
-        "load-json-file": "^1.0.0",
-        "normalize-package-data": "^2.3.2",
-        "path-type": "^1.0.0"
-      }
-    },
-    "read-pkg-up": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
-      "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
-      "dev": true,
-      "requires": {
-        "find-up": "^1.0.0",
-        "read-pkg": "^1.0.0"
-      }
-    },
-    "readable-stream": {
-      "version": "2.3.7",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
-      "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
-      "dev": true,
-      "requires": {
-        "core-util-is": "~1.0.0",
-        "inherits": "~2.0.3",
-        "isarray": "~1.0.0",
-        "process-nextick-args": "~2.0.0",
-        "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.1.1",
-        "util-deprecate": "~1.0.1"
-      }
-    },
-    "readdirp": {
-      "version": "3.3.0",
-      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.3.0.tgz",
-      "integrity": "sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ==",
-      "dev": true,
-      "requires": {
-        "picomatch": "^2.0.7"
-      }
-    },
-    "redent": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
-      "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
-      "dev": true,
-      "requires": {
-        "indent-string": "^2.1.0",
-        "strip-indent": "^1.0.1"
-      }
-    },
-    "regex-not": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
-      "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.2",
-        "safe-regex": "^1.1.0"
-      }
-    },
-    "remove-trailing-separator": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
-      "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=",
-      "dev": true
-    },
-    "repeat-element": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
-      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
-      "dev": true
-    },
-    "repeat-string": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz",
-      "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=",
-      "dev": true
-    },
-    "repeating": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
-      "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
-      "dev": true,
-      "requires": {
-        "is-finite": "^1.0.0"
-      }
-    },
-    "request": {
-      "version": "2.88.2",
-      "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
-      "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
-      "dev": true,
-      "requires": {
-        "aws-sign2": "~0.7.0",
-        "aws4": "^1.8.0",
-        "caseless": "~0.12.0",
-        "combined-stream": "~1.0.6",
-        "extend": "~3.0.2",
-        "forever-agent": "~0.6.1",
-        "form-data": "~2.3.2",
-        "har-validator": "~5.1.3",
-        "http-signature": "~1.2.0",
-        "is-typedarray": "~1.0.0",
-        "isstream": "~0.1.2",
-        "json-stringify-safe": "~5.0.1",
-        "mime-types": "~2.1.19",
-        "oauth-sign": "~0.9.0",
-        "performance-now": "^2.1.0",
-        "qs": "~6.5.2",
-        "safe-buffer": "^5.1.2",
-        "tough-cookie": "~2.5.0",
-        "tunnel-agent": "^0.6.0",
-        "uuid": "^3.3.2"
-      }
-    },
-    "require-directory": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
-      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
-      "dev": true
-    },
-    "require-main-filename": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz",
-      "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=",
-      "dev": true
-    },
-    "resolve-cwd": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
-      "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=",
-      "dev": true,
-      "requires": {
-        "resolve-from": "^3.0.0"
-      }
-    },
-    "resolve-dir": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
-      "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=",
-      "dev": true,
-      "requires": {
-        "expand-tilde": "^2.0.0",
-        "global-modules": "^1.0.0"
-      },
-      "dependencies": {
-        "global-modules": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
-          "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
-          "dev": true,
-          "requires": {
-            "global-prefix": "^1.0.1",
-            "is-windows": "^1.0.1",
-            "resolve-dir": "^1.0.0"
-          }
-        }
-      }
-    },
-    "resolve-from": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
-      "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=",
-      "dev": true
-    },
-    "resolve-url": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
-      "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
-      "dev": true
-    },
-    "ret": {
-      "version": "0.1.15",
-      "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
-      "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==",
-      "dev": true
-    },
-    "rimraf": {
-      "version": "2.7.1",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
-      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
-      "dev": true,
-      "requires": {
-        "glob": "^7.1.3"
-      }
-    },
-    "ripemd160": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz",
-      "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==",
-      "dev": true,
-      "requires": {
-        "hash-base": "^3.0.0",
-        "inherits": "^2.0.1"
-      }
-    },
-    "run-queue": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
-      "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=",
-      "dev": true,
-      "requires": {
-        "aproba": "^1.1.1"
-      }
-    },
-    "safe-buffer": {
-      "version": "5.1.2",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-      "dev": true
-    },
-    "safe-regex": {
-      "version": "1.1.0",
-      "resolved": "http://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz",
-      "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=",
-      "dev": true,
-      "requires": {
-        "ret": "~0.1.10"
-      }
-    },
-    "safer-buffer": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
-      "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
-      "dev": true
-    },
-    "sass-graph": {
-      "version": "2.2.4",
-      "resolved": "https://registry.npmjs.org/sass-graph/-/sass-graph-2.2.4.tgz",
-      "integrity": "sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=",
-      "dev": true,
-      "requires": {
-        "glob": "^7.0.0",
-        "lodash": "^4.0.0",
-        "scss-tokenizer": "^0.2.3",
-        "yargs": "^7.0.0"
-      }
-    },
-    "sass-loader": {
-      "version": "8.0.2",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.2.tgz",
-      "integrity": "sha512-7o4dbSK8/Ol2KflEmSco4jTjQoV988bM82P9CZdmo9hR3RLnvNc0ufMNdMrB0caq38JQ/FgF4/7RcbcfKzxoFQ==",
-      "dev": true,
-      "requires": {
-        "clone-deep": "^4.0.1",
-        "loader-utils": "^1.2.3",
-        "neo-async": "^2.6.1",
-        "schema-utils": "^2.6.1",
-        "semver": "^6.3.0"
-      },
-      "dependencies": {
-        "semver": {
-          "version": "6.3.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
-          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
-          "dev": true
-        }
-      }
-    },
-    "schema-utils": {
-      "version": "2.6.5",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.5.tgz",
-      "integrity": "sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==",
-      "dev": true,
-      "requires": {
-        "ajv": "^6.12.0",
-        "ajv-keywords": "^3.4.1"
-      },
-      "dependencies": {
-        "ajv": {
-          "version": "6.12.0",
-          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz",
-          "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==",
-          "dev": true,
-          "requires": {
-            "fast-deep-equal": "^3.1.1",
-            "fast-json-stable-stringify": "^2.0.0",
-            "json-schema-traverse": "^0.4.1",
-            "uri-js": "^4.2.2"
-          }
-        },
-        "fast-deep-equal": {
-          "version": "3.1.1",
-          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz",
-          "integrity": "sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==",
-          "dev": true
-        }
-      }
-    },
-    "scss-tokenizer": {
-      "version": "0.2.3",
-      "resolved": "https://registry.npmjs.org/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz",
-      "integrity": "sha1-jrBtualyMzOCTT9VMGQRSYR85dE=",
-      "dev": true,
-      "requires": {
-        "js-base64": "^2.1.8",
-        "source-map": "^0.4.2"
-      },
-      "dependencies": {
-        "source-map": {
-          "version": "0.4.4",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz",
-          "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=",
-          "dev": true,
-          "requires": {
-            "amdefine": ">=0.0.4"
-          }
-        }
-      }
-    },
-    "select": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
-      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
-    },
-    "semver": {
-      "version": "5.6.0",
-      "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
-      "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==",
-      "dev": true
-    },
-    "serialize-javascript": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
-      "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
-      "dev": true
-    },
-    "set-blocking": {
+    "isexe": {
       "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
-      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
-      "dev": true
-    },
-    "set-value": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz",
-      "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-extendable": "^0.1.1",
-        "is-plain-object": "^2.0.3",
-        "split-string": "^3.0.1"
-      },
-      "dependencies": {
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        }
-      }
-    },
-    "setimmediate": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
-      "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
       "dev": true
     },
-    "sha.js": {
-      "version": "2.4.11",
-      "resolved": "http://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz",
-      "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==",
-      "dev": true,
-      "requires": {
-        "inherits": "^2.0.1",
-        "safe-buffer": "^5.0.1"
-      }
-    },
-    "shallow-clone": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
-      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
-      "dev": true,
-      "requires": {
-        "kind-of": "^6.0.2"
-      }
-    },
-    "shebang-command": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
-      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
-      "dev": true,
-      "requires": {
-        "shebang-regex": "^1.0.0"
-      }
-    },
-    "shebang-regex": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
-      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+    "json-parse-better-errors": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
+      "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==",
       "dev": true
     },
-    "shell-quote": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.6.1.tgz",
-      "integrity": "sha1-9HgZSczkAmlxJ0MOo7PFR29IF2c=",
-      "dev": true,
-      "requires": {
-        "array-filter": "~0.0.0",
-        "array-map": "~0.0.0",
-        "array-reduce": "~0.0.0",
-        "jsonify": "~0.0.0"
-      }
-    },
-    "signal-exit": {
+    "linkify-it": {
       "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
-      "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=",
-      "dev": true
-    },
-    "snapdragon": {
-      "version": "0.8.2",
-      "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
-      "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==",
-      "dev": true,
-      "requires": {
-        "base": "^0.11.1",
-        "debug": "^2.2.0",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "map-cache": "^0.2.2",
-        "source-map": "^0.5.6",
-        "source-map-resolve": "^0.5.0",
-        "use": "^3.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "source-map": {
-          "version": "0.5.7",
-          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
-          "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
-          "dev": true
-        }
-      }
-    },
-    "snapdragon-node": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz",
-      "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==",
-      "dev": true,
-      "requires": {
-        "define-property": "^1.0.0",
-        "isobject": "^3.0.0",
-        "snapdragon-util": "^3.0.1"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "dev": true,
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        }
-      }
-    },
-    "snapdragon-util": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz",
-      "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==",
-      "dev": true,
-      "requires": {
-        "kind-of": "^3.2.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
-      }
-    },
-    "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.10.2",
-      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
-      "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
-    },
-    "source-list-map": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
-      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
-      "dev": true
-    },
-    "source-map": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
-      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "dev": true
-    },
-    "source-map-resolve": {
-      "version": "0.5.3",
-      "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz",
-      "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==",
-      "dev": true,
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
+      "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
       "requires": {
-        "atob": "^2.1.2",
-        "decode-uri-component": "^0.2.0",
-        "resolve-url": "^0.2.1",
-        "source-map-url": "^0.4.0",
-        "urix": "^0.1.0"
+        "uc.micro": "^1.0.1"
       }
     },
-    "source-map-support": {
-      "version": "0.5.16",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
-      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
+    "livereload": {
+      "version": "0.9.1",
+      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.1.tgz",
+      "integrity": "sha512-9g7sua11kkyZNo2hLRCG3LuZZwqexoyEyecSlV8cAsfAVVCZqLzVir6XDqmH0r+Vzgnd5LrdHDMyjtFnJQLAYw==",
       "dev": true,
       "requires": {
-        "buffer-from": "^1.0.0",
-        "source-map": "^0.6.0"
+        "chokidar": "^3.3.0",
+        "livereload-js": "^3.1.0",
+        "opts": ">= 1.2.0",
+        "ws": "^6.2.1"
       }
     },
-    "source-map-url": {
-      "version": "0.4.0",
-      "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz",
-      "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
+    "livereload-js": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.1.tgz",
+      "integrity": "sha512-CBu1gTEfzVhlOK1WASKAAJ9Qx1fHECTq0SUB67sfxwQssopTyvzqTlgl+c0h9pZ6V+Fzd2rc510ppuNusg9teQ==",
       "dev": true
     },
-    "spdx-correct": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.0.2.tgz",
-      "integrity": "sha512-q9hedtzyXHr5S0A1vEPoK/7l8NpfkFYTq6iCY+Pno2ZbdZR6WexZFtqeVGkGxW3TEJMN914Z55EnAGMmenlIQQ==",
+    "load-json-file": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz",
+      "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=",
       "dev": true,
       "requires": {
-        "spdx-expression-parse": "^3.0.0",
-        "spdx-license-ids": "^3.0.0"
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
       }
     },
-    "spdx-exceptions": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz",
-      "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==",
-      "dev": true
-    },
-    "spdx-expression-parse": {
+    "locate-path": {
       "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz",
-      "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==",
-      "dev": true,
-      "requires": {
-        "spdx-exceptions": "^2.1.0",
-        "spdx-license-ids": "^3.0.0"
-      }
-    },
-    "spdx-license-ids": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.2.tgz",
-      "integrity": "sha512-qky9CVt0lVIECkEsYbNILVnPvycuEBkXoMFLRWsREkomQLevYhtRKC+R91a5TOAQ3bCMjikRwhyaRqj1VYatYg==",
-      "dev": true
-    },
-    "split-string": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
-      "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==",
-      "dev": true,
-      "requires": {
-        "extend-shallow": "^3.0.0"
-      }
-    },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
-    },
-    "sshpk": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
-      "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
-      "dev": true,
-      "requires": {
-        "asn1": "~0.2.3",
-        "assert-plus": "^1.0.0",
-        "bcrypt-pbkdf": "^1.0.0",
-        "dashdash": "^1.12.0",
-        "ecc-jsbn": "~0.1.1",
-        "getpass": "^0.1.1",
-        "jsbn": "~0.1.0",
-        "safer-buffer": "^2.0.2",
-        "tweetnacl": "~0.14.0"
-      }
-    },
-    "ssri": {
-      "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": {
-        "figgy-pudding": "^3.5.1"
-      }
-    },
-    "static-extend": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz",
-      "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=",
-      "dev": true,
-      "requires": {
-        "define-property": "^0.2.5",
-        "object-copy": "^0.1.0"
-      },
-      "dependencies": {
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^0.1.0"
-          }
-        }
-      }
-    },
-    "stdout-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/stdout-stream/-/stdout-stream-1.4.1.tgz",
-      "integrity": "sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==",
-      "dev": true,
-      "requires": {
-        "readable-stream": "^2.0.1"
-      }
-    },
-    "stream-browserify": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz",
-      "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==",
-      "dev": true,
-      "requires": {
-        "inherits": "~2.0.1",
-        "readable-stream": "^2.0.2"
-      }
-    },
-    "stream-each": {
-      "version": "1.2.3",
-      "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz",
-      "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==",
-      "dev": true,
-      "requires": {
-        "end-of-stream": "^1.1.0",
-        "stream-shift": "^1.0.0"
-      }
-    },
-    "stream-http": {
-      "version": "2.8.3",
-      "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
-      "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+      "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
       "dev": true,
       "requires": {
-        "builtin-status-codes": "^3.0.0",
-        "inherits": "^2.0.1",
-        "readable-stream": "^2.3.6",
-        "to-arraybuffer": "^1.0.0",
-        "xtend": "^4.0.0"
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
       }
     },
-    "stream-shift": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
-      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
+    "lodash.debounce": {
+      "version": "4.0.8",
+      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=",
       "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=",
+    "lodash.throttle": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
+      "integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=",
       "dev": true
     },
-    "string-width": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
-      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
-      "dev": true,
-      "requires": {
-        "code-point-at": "^1.0.0",
-        "is-fullwidth-code-point": "^1.0.0",
-        "strip-ansi": "^3.0.0"
-      }
-    },
-    "string.prototype.padend": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.0.0.tgz",
-      "integrity": "sha1-86rvfBcZ8XDF6rHDK/eA2W4h8vA=",
-      "dev": true,
-      "requires": {
-        "define-properties": "^1.1.2",
-        "es-abstract": "^1.4.3",
-        "function-bind": "^1.0.2"
-      }
-    },
-    "string_decoder": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-      "dev": true,
-      "requires": {
-        "safe-buffer": "~5.1.0"
-      }
-    },
-    "strip-ansi": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
-      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
-      "dev": true,
-      "requires": {
-        "ansi-regex": "^2.0.0"
-      }
-    },
-    "strip-bom": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
-      "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
-      "dev": true,
+    "markdown-it": {
+      "version": "11.0.0",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.0.tgz",
+      "integrity": "sha512-+CvOnmbSubmQFSA9dKz1BRiaSMV7rhexl3sngKqFyXSagoA3fBdJQ8oZWtRy2knXdpDXaBw44euz37DeJQ9asg==",
       "requires": {
-        "is-utf8": "^0.2.0"
+        "argparse": "^1.0.7",
+        "entities": "~2.0.0",
+        "linkify-it": "^3.0.1",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
       }
     },
-    "strip-eof": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
-      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
-      "dev": true
+    "markdown-it-task-lists": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+      "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA=="
     },
-    "strip-indent": {
+    "mdurl": {
       "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
-      "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
-      "dev": true,
-      "requires": {
-        "get-stdin": "^4.0.1"
-      }
-    },
-    "style-loader": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.3.tgz",
-      "integrity": "sha512-rlkH7X/22yuwFYK357fMN/BxYOorfnfq0eD7+vqlemSK4wEcejFF1dg4zxP0euBW8NrYx2WZzZ8PPFevr7D+Kw==",
-      "dev": true,
-      "requires": {
-        "loader-utils": "^1.2.3",
-        "schema-utils": "^2.6.4"
-      }
-    },
-    "supports-color": {
-      "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"
-      }
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
     },
-    "tapable": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
-      "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==",
+    "memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
       "dev": true
     },
-    "tar": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz",
-      "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==",
-      "dev": true,
-      "requires": {
-        "block-stream": "*",
-        "fstream": "^1.0.12",
-        "inherits": "2"
-      }
-    },
-    "terser": {
-      "version": "4.6.10",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.10.tgz",
-      "integrity": "sha512-qbF/3UOo11Hggsbsqm2hPa6+L4w7bkr+09FNseEe8xrcVD3APGLFqE+Oz1ZKAxjYnFsj80rLOfgAtJ0LNJjtTA==",
+    "minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
       "dev": true,
       "requires": {
-        "commander": "^2.20.0",
-        "source-map": "~0.6.1",
-        "source-map-support": "~0.5.12"
+        "brace-expansion": "^1.1.7"
       }
     },
-    "terser-webpack-plugin": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
-      "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
-      "dev": true,
-      "requires": {
-        "cacache": "^12.0.2",
-        "find-cache-dir": "^2.1.0",
-        "is-wsl": "^1.1.0",
-        "schema-utils": "^1.0.0",
-        "serialize-javascript": "^2.1.2",
-        "source-map": "^0.6.1",
-        "terser": "^4.1.2",
-        "webpack-sources": "^1.4.0",
-        "worker-farm": "^1.7.0"
-      },
-      "dependencies": {
-        "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"
-          }
-        }
-      }
+    "nice-try": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
+      "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==",
+      "dev": true
     },
-    "through2": {
-      "version": "2.0.5",
-      "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
-      "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+    "normalize-package-data": {
+      "version": "2.5.0",
+      "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
+      "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
       "dev": true,
       "requires": {
-        "readable-stream": "~2.3.6",
-        "xtend": "~4.0.1"
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
       }
     },
-    "timers-browserify": {
-      "version": "2.0.11",
-      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
-      "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "npm-run-all": {
+      "version": "4.1.5",
+      "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz",
+      "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==",
       "dev": true,
       "requires": {
-        "setimmediate": "^1.0.4"
+        "ansi-styles": "^3.2.1",
+        "chalk": "^2.4.1",
+        "cross-spawn": "^6.0.5",
+        "memorystream": "^0.3.1",
+        "minimatch": "^3.0.4",
+        "pidtree": "^0.3.0",
+        "read-pkg": "^3.0.0",
+        "shell-quote": "^1.6.1",
+        "string.prototype.padend": "^3.0.0"
       }
     },
-    "tiny-emitter": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
-      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    "object-inspect": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
+      "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
+      "dev": true
     },
-    "to-arraybuffer": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
-      "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=",
+    "object-keys": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+      "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
       "dev": true
     },
-    "to-object-path": {
-      "version": "0.3.0",
-      "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz",
-      "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=",
+    "object.assign": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz",
+      "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==",
       "dev": true,
       "requires": {
-        "kind-of": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        }
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
       }
     },
-    "to-regex": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz",
-      "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==",
-      "dev": true,
-      "requires": {
-        "define-property": "^2.0.2",
-        "extend-shallow": "^3.0.2",
-        "regex-not": "^1.0.2",
-        "safe-regex": "^1.1.0"
-      }
+    "opts": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
+      "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
+      "dev": true
     },
-    "to-regex-range": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
-      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+    "p-limit": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+      "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
       "dev": true,
       "requires": {
-        "is-number": "^7.0.0"
+        "p-try": "^2.0.0"
       }
     },
-    "tough-cookie": {
-      "version": "2.5.0",
-      "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
-      "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
+    "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": {
-        "psl": "^1.1.28",
-        "punycode": "^2.1.1"
+        "p-limit": "^2.0.0"
       }
     },
-    "trim-newlines": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
-      "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM=",
+    "p-try": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+      "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
       "dev": true
     },
-    "true-case-path": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/true-case-path/-/true-case-path-1.0.3.tgz",
-      "integrity": "sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==",
+    "parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
       "dev": true,
       "requires": {
-        "glob": "^7.1.2"
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
       }
     },
-    "tslib": {
-      "version": "1.11.1",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
-      "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==",
+    "path-exists": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+      "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+      "dev": true
+    },
+    "path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
       "dev": true
     },
-    "tty-browserify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
-      "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=",
+    "path-parse": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
+      "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==",
       "dev": true
     },
-    "tunnel-agent": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
-      "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+    "path-type": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz",
+      "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==",
       "dev": true,
       "requires": {
-        "safe-buffer": "^5.0.1"
+        "pify": "^3.0.0"
       }
     },
-    "tweetnacl": {
-      "version": "0.14.5",
-      "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
-      "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+    "picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
       "dev": true
     },
-    "typedarray": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
-      "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=",
+    "pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
       "dev": true
     },
-    "uc.micro": {
-      "version": "1.0.6",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
-      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+    "pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+      "dev": true
     },
-    "union-value": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz",
-      "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==",
+    "punycode": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
+      "dev": true
+    },
+    "read-pkg": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",
+      "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=",
       "dev": true,
       "requires": {
-        "arr-union": "^3.1.0",
-        "get-value": "^2.0.6",
-        "is-extendable": "^0.1.1",
-        "set-value": "^2.0.1"
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
       }
     },
-    "uniq": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
-      "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=",
-      "dev": true
-    },
-    "unique-filename": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
-      "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==",
+    "readdirp": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz",
+      "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==",
       "dev": true,
       "requires": {
-        "unique-slug": "^2.0.0"
+        "picomatch": "^2.2.1"
       }
     },
-    "unique-slug": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
-      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
+      "dev": true
+    },
+    "require-main-filename": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+      "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+      "dev": true
+    },
+    "resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
       "dev": true,
       "requires": {
-        "imurmurhash": "^0.1.4"
+        "path-parse": "^1.0.6"
       }
     },
-    "unset-value": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz",
-      "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=",
+    "sass": {
+      "version": "1.26.10",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.26.10.tgz",
+      "integrity": "sha512-bzN0uvmzfsTvjz0qwccN1sPm2HxxpNI/Xa+7PlUEMS+nQvbyuEK7Y0qFqxlPHhiNHb1Ze8WQJtU31olMObkAMw==",
       "dev": true,
       "requires": {
-        "has-value": "^0.3.1",
-        "isobject": "^3.0.0"
-      },
-      "dependencies": {
-        "has-value": {
-          "version": "0.3.1",
-          "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
-          "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=",
-          "dev": true,
-          "requires": {
-            "get-value": "^2.0.3",
-            "has-values": "^0.1.4",
-            "isobject": "^2.0.0"
-          },
-          "dependencies": {
-            "isobject": {
-              "version": "2.1.0",
-              "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
-              "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=",
-              "dev": true,
-              "requires": {
-                "isarray": "1.0.0"
-              }
-            }
-          }
-        },
-        "has-values": {
-          "version": "0.1.4",
-          "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
-          "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=",
-          "dev": true
-        }
+        "chokidar": ">=2.0.0 <4.0.0"
       }
     },
-    "upath": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
-      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+    "select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+    },
+    "semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true
+    },
+    "set-blocking": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
+      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=",
       "dev": true
     },
-    "uri-js": {
-      "version": "4.2.2",
-      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
-      "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
+    "shebang-command": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz",
+      "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=",
       "dev": true,
       "requires": {
-        "punycode": "^2.1.0"
+        "shebang-regex": "^1.0.0"
       }
     },
-    "urix": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz",
-      "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=",
+    "shebang-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz",
+      "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=",
+      "dev": true
+    },
+    "shell-quote": {
+      "version": "1.7.2",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz",
+      "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==",
       "dev": true
     },
-    "url": {
-      "version": "0.11.0",
-      "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
-      "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
+    "sortablejs": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
+      "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
+    },
+    "spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
       "dev": true,
       "requires": {
-        "punycode": "1.3.2",
-        "querystring": "0.2.0"
-      },
-      "dependencies": {
-        "punycode": {
-          "version": "1.3.2",
-          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
-          "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=",
-          "dev": true
-        }
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "use": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
-      "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==",
+    "spdx-exceptions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+      "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
       "dev": true
     },
-    "util": {
-      "version": "0.11.1",
-      "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
-      "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
+    "spdx-expression-parse": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+      "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
       "dev": true,
       "requires": {
-        "inherits": "2.0.3"
-      },
-      "dependencies": {
-        "inherits": {
-          "version": "2.0.3",
-          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
-          "dev": true
-        }
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
       }
     },
-    "util-deprecate": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
-      "dev": true
-    },
-    "uuid": {
-      "version": "3.4.0",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
-      "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
+    "spdx-license-ids": {
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz",
+      "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
       "dev": true
     },
-    "v8-compile-cache": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz",
-      "integrity": "sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w==",
-      "dev": true
+    "sprintf-js": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
-    "validate-npm-package-license": {
-      "version": "3.0.4",
-      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
-      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+    "string-width": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+      "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
       "dev": true,
       "requires": {
-        "spdx-correct": "^3.0.0",
-        "spdx-expression-parse": "^3.0.0"
+        "emoji-regex": "^7.0.1",
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^5.1.0"
       }
     },
-    "verror": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
-      "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+    "string.prototype.padend": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz",
+      "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==",
       "dev": true,
       "requires": {
-        "assert-plus": "^1.0.0",
-        "core-util-is": "1.0.2",
-        "extsprintf": "^1.2.0"
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
       }
     },
-    "vm-browserify": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
-      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
-      "dev": true
-    },
-    "vue": {
-      "version": "2.6.11",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
-      "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
+    "string.prototype.trimend": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
+      "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
+      "dev": true,
+      "requires": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
     },
-    "vuedraggable": {
-      "version": "2.23.2",
-      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.2.tgz",
-      "integrity": "sha512-PgHCjUpxEAEZJq36ys49HfQmXglattf/7ofOzUrW2/rRdG7tu6fK84ir14t1jYv4kdXewTEa2ieKEAhhEMdwkQ==",
+    "string.prototype.trimstart": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
+      "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
+      "dev": true,
       "requires": {
-        "sortablejs": "^1.10.1"
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
       }
     },
-    "watchpack": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz",
-      "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==",
+    "strip-ansi": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+      "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
       "dev": true,
       "requires": {
-        "chokidar": "^2.1.8",
-        "graceful-fs": "^4.1.2",
-        "neo-async": "^2.5.0"
-      },
-      "dependencies": {
-        "anymatch": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
-          "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
-          "dev": true,
-          "requires": {
-            "micromatch": "^3.1.4",
-            "normalize-path": "^2.1.1"
-          },
-          "dependencies": {
-            "normalize-path": {
-              "version": "2.1.1",
-              "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
-              "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
-              "dev": true,
-              "requires": {
-                "remove-trailing-separator": "^1.0.1"
-              }
-            }
-          }
-        },
-        "binary-extensions": {
-          "version": "1.13.1",
-          "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
-          "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
-          "dev": true
-        },
-        "braces": {
-          "version": "2.3.2",
-          "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
-          "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
-          "dev": true,
-          "requires": {
-            "arr-flatten": "^1.1.0",
-            "array-unique": "^0.3.2",
-            "extend-shallow": "^2.0.1",
-            "fill-range": "^4.0.0",
-            "isobject": "^3.0.1",
-            "repeat-element": "^1.1.2",
-            "snapdragon": "^0.8.1",
-            "snapdragon-node": "^2.0.1",
-            "split-string": "^3.0.2",
-            "to-regex": "^3.0.1"
-          }
-        },
-        "chokidar": {
-          "version": "2.1.8",
-          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
-          "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
-          "dev": true,
-          "requires": {
-            "anymatch": "^2.0.0",
-            "async-each": "^1.0.1",
-            "braces": "^2.3.2",
-            "fsevents": "^1.2.7",
-            "glob-parent": "^3.1.0",
-            "inherits": "^2.0.3",
-            "is-binary-path": "^1.0.0",
-            "is-glob": "^4.0.0",
-            "normalize-path": "^3.0.0",
-            "path-is-absolute": "^1.0.0",
-            "readdirp": "^2.2.1",
-            "upath": "^1.1.1"
-          }
-        },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
-          "dev": true,
-          "requires": {
-            "is-extendable": "^0.1.0"
-          }
-        },
-        "fill-range": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-          "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
-          "dev": true,
-          "requires": {
-            "extend-shallow": "^2.0.1",
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1",
-            "to-regex-range": "^2.1.0"
-          }
-        },
-        "fsevents": {
-          "version": "1.2.12",
-          "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.12.tgz",
-          "integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "nan": "^2.12.1",
-            "node-pre-gyp": "*"
-          },
-          "dependencies": {
-            "abbrev": {
-              "version": "1.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "ansi-regex": {
-              "version": "2.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "aproba": {
-              "version": "1.2.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "are-we-there-yet": {
-              "version": "1.1.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "delegates": "^1.0.0",
-                "readable-stream": "^2.0.6"
-              }
-            },
-            "balanced-match": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "brace-expansion": {
-              "version": "1.1.11",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
-              }
-            },
-            "chownr": {
-              "version": "1.1.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "code-point-at": {
-              "version": "1.1.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "concat-map": {
-              "version": "0.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "console-control-strings": {
-              "version": "1.1.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "core-util-is": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "debug": {
-              "version": "3.2.6",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "ms": "^2.1.1"
-              }
-            },
-            "deep-extend": {
-              "version": "0.6.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "delegates": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "detect-libc": {
-              "version": "1.0.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "fs-minipass": {
-              "version": "1.2.7",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minipass": "^2.6.0"
-              }
-            },
-            "fs.realpath": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "gauge": {
-              "version": "2.7.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "aproba": "^1.0.3",
-                "console-control-strings": "^1.0.0",
-                "has-unicode": "^2.0.0",
-                "object-assign": "^4.1.0",
-                "signal-exit": "^3.0.0",
-                "string-width": "^1.0.1",
-                "strip-ansi": "^3.0.1",
-                "wide-align": "^1.1.0"
-              }
-            },
-            "glob": {
-              "version": "7.1.6",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^3.0.4",
-                "once": "^1.3.0",
-                "path-is-absolute": "^1.0.0"
-              }
-            },
-            "has-unicode": {
-              "version": "2.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "iconv-lite": {
-              "version": "0.4.24",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "safer-buffer": ">= 2.1.2 < 3"
-              }
-            },
-            "ignore-walk": {
-              "version": "3.0.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minimatch": "^3.0.4"
-              }
-            },
-            "inflight": {
-              "version": "1.0.6",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "once": "^1.3.0",
-                "wrappy": "1"
-              }
-            },
-            "inherits": {
-              "version": "2.0.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "ini": {
-              "version": "1.3.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "is-fullwidth-code-point": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "number-is-nan": "^1.0.0"
-              }
-            },
-            "isarray": {
-              "version": "1.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "minimatch": {
-              "version": "3.0.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "brace-expansion": "^1.1.7"
-              }
-            },
-            "minimist": {
-              "version": "1.2.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "minipass": {
-              "version": "2.9.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "safe-buffer": "^5.1.2",
-                "yallist": "^3.0.0"
-              }
-            },
-            "minizlib": {
-              "version": "1.3.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minipass": "^2.9.0"
-              }
-            },
-            "mkdirp": {
-              "version": "0.5.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "minimist": "^1.2.5"
-              }
-            },
-            "ms": {
-              "version": "2.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "needle": {
-              "version": "2.3.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "debug": "^3.2.6",
-                "iconv-lite": "^0.4.4",
-                "sax": "^1.2.4"
-              }
-            },
-            "node-pre-gyp": {
-              "version": "0.14.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "detect-libc": "^1.0.2",
-                "mkdirp": "^0.5.1",
-                "needle": "^2.2.1",
-                "nopt": "^4.0.1",
-                "npm-packlist": "^1.1.6",
-                "npmlog": "^4.0.2",
-                "rc": "^1.2.7",
-                "rimraf": "^2.6.1",
-                "semver": "^5.3.0",
-                "tar": "^4.4.2"
-              }
-            },
-            "nopt": {
-              "version": "4.0.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "abbrev": "1",
-                "osenv": "^0.1.4"
-              }
-            },
-            "npm-bundled": {
-              "version": "1.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "npm-normalize-package-bin": "^1.0.1"
-              }
-            },
-            "npm-normalize-package-bin": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "npm-packlist": {
-              "version": "1.4.8",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "ignore-walk": "^3.0.1",
-                "npm-bundled": "^1.0.1",
-                "npm-normalize-package-bin": "^1.0.1"
-              }
-            },
-            "npmlog": {
-              "version": "4.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "are-we-there-yet": "~1.1.2",
-                "console-control-strings": "~1.1.0",
-                "gauge": "~2.7.3",
-                "set-blocking": "~2.0.0"
-              }
-            },
-            "number-is-nan": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "object-assign": {
-              "version": "4.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "once": {
-              "version": "1.4.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "wrappy": "1"
-              }
-            },
-            "os-homedir": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "os-tmpdir": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "osenv": {
-              "version": "0.1.5",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "os-homedir": "^1.0.0",
-                "os-tmpdir": "^1.0.0"
-              }
-            },
-            "path-is-absolute": {
-              "version": "1.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "process-nextick-args": {
-              "version": "2.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "rc": {
-              "version": "1.2.8",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "deep-extend": "^0.6.0",
-                "ini": "~1.3.0",
-                "minimist": "^1.2.0",
-                "strip-json-comments": "~2.0.1"
-              }
-            },
-            "readable-stream": {
-              "version": "2.3.7",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "core-util-is": "~1.0.0",
-                "inherits": "~2.0.3",
-                "isarray": "~1.0.0",
-                "process-nextick-args": "~2.0.0",
-                "safe-buffer": "~5.1.1",
-                "string_decoder": "~1.1.1",
-                "util-deprecate": "~1.0.1"
-              }
-            },
-            "rimraf": {
-              "version": "2.7.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "glob": "^7.1.3"
-              }
-            },
-            "safe-buffer": {
-              "version": "5.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "safer-buffer": {
-              "version": "2.1.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "sax": {
-              "version": "1.2.4",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "semver": {
-              "version": "5.7.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "set-blocking": {
-              "version": "2.0.0",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "signal-exit": {
-              "version": "3.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "string-width": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "code-point-at": "^1.0.0",
-                "is-fullwidth-code-point": "^1.0.0",
-                "strip-ansi": "^3.0.0"
-              }
-            },
-            "string_decoder": {
-              "version": "1.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "safe-buffer": "~5.1.0"
-              }
-            },
-            "strip-ansi": {
-              "version": "3.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "ansi-regex": "^2.0.0"
-              }
-            },
-            "strip-json-comments": {
-              "version": "2.0.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "tar": {
-              "version": "4.4.13",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "chownr": "^1.1.1",
-                "fs-minipass": "^1.2.5",
-                "minipass": "^2.8.6",
-                "minizlib": "^1.2.1",
-                "mkdirp": "^0.5.0",
-                "safe-buffer": "^5.1.2",
-                "yallist": "^3.0.3"
-              }
-            },
-            "util-deprecate": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "wide-align": {
-              "version": "1.1.3",
-              "bundled": true,
-              "dev": true,
-              "optional": true,
-              "requires": {
-                "string-width": "^1.0.2 || 2"
-              }
-            },
-            "wrappy": {
-              "version": "1.0.2",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            },
-            "yallist": {
-              "version": "3.1.1",
-              "bundled": true,
-              "dev": true,
-              "optional": true
-            }
-          }
-        },
-        "glob-parent": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
-          "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
-          "dev": true,
-          "requires": {
-            "is-glob": "^3.1.0",
-            "path-dirname": "^1.0.0"
-          },
-          "dependencies": {
-            "is-glob": {
-              "version": "3.1.0",
-              "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-              "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-              "dev": true,
-              "requires": {
-                "is-extglob": "^2.1.0"
-              }
-            }
-          }
-        },
-        "is-binary-path": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
-          "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
-          "dev": true,
-          "requires": {
-            "binary-extensions": "^1.0.0"
-          }
-        },
-        "is-number": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
-          "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
-          "dev": true,
-          "requires": {
-            "kind-of": "^3.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "3.2.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-          "dev": true,
-          "requires": {
-            "is-buffer": "^1.1.5"
-          }
-        },
-        "readdirp": {
-          "version": "2.2.1",
-          "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
-          "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.11",
-            "micromatch": "^3.1.10",
-            "readable-stream": "^2.0.2"
-          }
-        },
-        "to-regex-range": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
-          "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
-          "dev": true,
-          "requires": {
-            "is-number": "^3.0.0",
-            "repeat-string": "^1.6.1"
-          }
-        }
+        "ansi-regex": "^4.1.0"
       }
     },
-    "webpack": {
-      "version": "4.42.1",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.42.1.tgz",
-      "integrity": "sha512-SGfYMigqEfdGchGhFFJ9KyRpQKnipvEvjc1TwrXEPCM6H5Wywu10ka8o3KGrMzSMxMQKt8aCHUFh5DaQ9UmyRg==",
+    "strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "5.5.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+      "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
       "dev": true,
       "requires": {
-        "@webassemblyjs/ast": "1.9.0",
-        "@webassemblyjs/helper-module-context": "1.9.0",
-        "@webassemblyjs/wasm-edit": "1.9.0",
-        "@webassemblyjs/wasm-parser": "1.9.0",
-        "acorn": "^6.2.1",
-        "ajv": "^6.10.2",
-        "ajv-keywords": "^3.4.1",
-        "chrome-trace-event": "^1.0.2",
-        "enhanced-resolve": "^4.1.0",
-        "eslint-scope": "^4.0.3",
-        "json-parse-better-errors": "^1.0.2",
-        "loader-runner": "^2.4.0",
-        "loader-utils": "^1.2.3",
-        "memory-fs": "^0.4.1",
-        "micromatch": "^3.1.10",
-        "mkdirp": "^0.5.3",
-        "neo-async": "^2.6.1",
-        "node-libs-browser": "^2.2.1",
-        "schema-utils": "^1.0.0",
-        "tapable": "^1.1.3",
-        "terser-webpack-plugin": "^1.4.3",
-        "watchpack": "^1.6.0",
-        "webpack-sources": "^1.4.1"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.5",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
-          "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
-          "dev": true
-        },
-        "mkdirp": {
-          "version": "0.5.5",
-          "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
-          "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.5"
-          }
-        },
-        "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"
-          }
-        }
+        "has-flag": "^3.0.0"
       }
     },
-    "webpack-cli": {
-      "version": "3.3.11",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.11.tgz",
-      "integrity": "sha512-dXlfuml7xvAFwYUPsrtQAA9e4DOe58gnzSxhgrO/ZM/gyXTBowrsYeubyN4mqGhYdpXMFNyQ6emjJS9M7OBd4g==",
+    "tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
       "dev": true,
       "requires": {
-        "chalk": "2.4.2",
-        "cross-spawn": "6.0.5",
-        "enhanced-resolve": "4.1.0",
-        "findup-sync": "3.0.0",
-        "global-modules": "2.0.0",
-        "import-local": "2.0.0",
-        "interpret": "1.2.0",
-        "loader-utils": "1.2.3",
-        "supports-color": "6.1.0",
-        "v8-compile-cache": "2.0.3",
-        "yargs": "13.2.4"
-      },
-      "dependencies": {
-        "ansi-regex": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
-          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
-          "dev": true
-        },
-        "chalk": {
-          "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": "^3.2.1",
-            "escape-string-regexp": "^1.0.5",
-            "supports-color": "^5.3.0"
-          },
-          "dependencies": {
-            "supports-color": {
-              "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"
-              }
-            }
-          }
-        },
-        "cliui": {
-          "version": "5.0.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
-          "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
-          "dev": true,
-          "requires": {
-            "string-width": "^3.1.0",
-            "strip-ansi": "^5.2.0",
-            "wrap-ansi": "^5.1.0"
-          }
-        },
-        "cross-spawn": {
-          "version": "6.0.5",
-          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
-          "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
-          "dev": true,
-          "requires": {
-            "nice-try": "^1.0.4",
-            "path-key": "^2.0.1",
-            "semver": "^5.5.0",
-            "shebang-command": "^1.2.0",
-            "which": "^1.2.9"
-          }
-        },
-        "enhanced-resolve": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
-          "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
-          "dev": true,
-          "requires": {
-            "graceful-fs": "^4.1.2",
-            "memory-fs": "^0.4.0",
-            "tapable": "^1.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"
-          }
-        },
-        "get-caller-file": {
-          "version": "2.0.5",
-          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
-          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
-          "dev": true
-        },
-        "invert-kv": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
-          "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
-          "dev": true
-        },
-        "is-fullwidth-code-point": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
-          "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=",
-          "dev": true
-        },
-        "lcid": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz",
-          "integrity": "sha512-avPEb8P8EGnwXKClwsNUgryVjllcRqtMYa49NTsbQagYuT1DcXnl1915oxWjoyGrXR6zH/Y0Zc96xWsPcoDKeA==",
-          "dev": true,
-          "requires": {
-            "invert-kv": "^2.0.0"
-          }
-        },
-        "os-locale": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
-          "integrity": "sha512-Z8l3R4wYWM40/52Z+S265okfFj8Kt2cC2MKY+xNi3kFs+XGI7WXu/I309QQQYbRW4ijiZ+yxs9pqEhJh0DqW3Q==",
-          "dev": true,
-          "requires": {
-            "execa": "^1.0.0",
-            "lcid": "^2.0.0",
-            "mem": "^4.0.0"
-          }
-        },
-        "require-main-filename": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
-          "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
-          "dev": true
-        },
-        "string-width": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
-          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
-          "dev": true,
-          "requires": {
-            "emoji-regex": "^7.0.1",
-            "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^5.1.0"
-          }
-        },
-        "strip-ansi": {
-          "version": "5.2.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
-          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
-          "dev": true,
-          "requires": {
-            "ansi-regex": "^4.1.0"
-          }
-        },
-        "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": {
-            "has-flag": "^3.0.0"
-          }
-        },
-        "which-module": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
-          "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
-          "dev": true
-        },
-        "wrap-ansi": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
-          "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
-          "dev": true,
-          "requires": {
-            "ansi-styles": "^3.2.0",
-            "string-width": "^3.0.0",
-            "strip-ansi": "^5.0.0"
-          }
-        },
-        "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
-        },
-        "yargs": {
-          "version": "13.2.4",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
-          "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
-          "dev": true,
-          "requires": {
-            "cliui": "^5.0.0",
-            "find-up": "^3.0.0",
-            "get-caller-file": "^2.0.1",
-            "os-locale": "^3.1.0",
-            "require-directory": "^2.1.1",
-            "require-main-filename": "^2.0.0",
-            "set-blocking": "^2.0.0",
-            "string-width": "^3.0.0",
-            "which-module": "^2.0.0",
-            "y18n": "^4.0.0",
-            "yargs-parser": "^13.1.0"
-          }
-        },
-        "yargs-parser": {
-          "version": "13.1.2",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
-          "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
-          "dev": true,
-          "requires": {
-            "camelcase": "^5.0.0",
-            "decamelize": "^1.2.0"
-          }
-        }
+        "is-number": "^7.0.0"
       }
     },
-    "webpack-sources": {
-      "version": "1.4.3",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
-      "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
+    "uc.micro": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
+    },
+    "validate-npm-package-license": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+      "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
       "dev": true,
       "requires": {
-        "source-list-map": "^2.0.0",
-        "source-map": "~0.6.1"
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
       }
     },
     "which": {
       }
     },
     "which-module": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz",
-      "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz",
+      "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
       "dev": true
     },
-    "wide-align": {
-      "version": "1.1.3",
-      "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
-      "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
-      "dev": true,
-      "requires": {
-        "string-width": "^1.0.2 || 2"
-      }
-    },
-    "worker-farm": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz",
-      "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==",
-      "dev": true,
-      "requires": {
-        "errno": "~0.1.7"
-      }
-    },
     "wrap-ansi": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
-      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+      "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
       "dev": true,
       "requires": {
-        "string-width": "^1.0.1",
-        "strip-ansi": "^3.0.1"
+        "ansi-styles": "^3.2.0",
+        "string-width": "^3.0.0",
+        "strip-ansi": "^5.0.0"
       }
     },
-    "wrappy": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-      "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
-      "dev": true
-    },
     "ws": {
       "version": "6.2.1",
       "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
         "async-limiter": "~1.0.0"
       }
     },
-    "xtend": {
-      "version": "4.0.2",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
-      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
-      "dev": true
-    },
     "y18n": {
-      "version": "3.2.1",
-      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
-      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
-      "dev": true
-    },
-    "yallist": {
-      "version": "2.1.2",
-      "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
-      "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=",
+      "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
     },
     "yargs": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.0.tgz",
-      "integrity": "sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=",
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0",
-        "cliui": "^3.2.0",
-        "decamelize": "^1.1.1",
-        "get-caller-file": "^1.0.1",
-        "os-locale": "^1.4.0",
-        "read-pkg-up": "^1.0.1",
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
         "require-directory": "^2.1.1",
-        "require-main-filename": "^1.0.1",
+        "require-main-filename": "^2.0.0",
         "set-blocking": "^2.0.0",
-        "string-width": "^1.0.2",
-        "which-module": "^1.0.0",
-        "y18n": "^3.2.1",
-        "yargs-parser": "^5.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true
-        }
+        "string-width": "^3.0.0",
+        "which-module": "^2.0.0",
+        "y18n": "^4.0.0",
+        "yargs-parser": "^13.1.2"
       }
     },
     "yargs-parser": {
-      "version": "5.0.0",
-      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.0.tgz",
-      "integrity": "sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=",
+      "version": "13.1.2",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+      "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
       "dev": true,
       "requires": {
-        "camelcase": "^3.0.0"
-      },
-      "dependencies": {
-        "camelcase": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz",
-          "integrity": "sha1-MvxLn82vhF/N9+c7uXysImHwqwo=",
-          "dev": true
-        }
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
       }
     }
   }
index a05851987d10486b08901e55e9b82e9b4fbfa08e..a8ec0df93d45591a493fd1d4fda4c862b9cfbd71 100644 (file)
@@ -1,36 +1,33 @@
 {
   "private": true,
   "scripts": {
-    "build": "webpack",
-    "production": "NODE_ENV=production webpack && rm -f ./public/dist/*styles.js",
-    "build-profile": "NODE_ENV=production webpack --profile --json > webpack-stats.json && rm -f ./public/dist/*styles.js",
+    "build:css:dev": "sass ./resources/sass:./public/dist",
+    "build:css:watch": "sass ./resources/sass:./public/dist --watch",
+    "build:css:production": "sass ./resources/sass:./public/dist -s compressed",
+    "build:js:dev": "esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --target=es2020",
+    "build:js:watch": "chokidar \"./resources/**/*.js\" -c \"npm run build:js:dev\"",
+    "build:js:production": "NODE_ENV=production esbuild --bundle ./resources/js/index.js --outfile=public/dist/app.js --sourcemap --minify",
+    "build": "npm-run-all --parallel build:*:dev",
+    "production": "npm-run-all --parallel build:*:production",
     "dev": "npm-run-all --parallel watch livereload",
-    "watch": "webpack --watch",
+    "watch": "npm-run-all --parallel build:*:watch",
     "livereload": "livereload ./public/dist/",
     "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
   },
   "devDependencies": {
-    "css-loader": "^3.4.2",
+    "chokidar-cli": "^2.1.0",
+    "esbuild": "0.6.30",
     "livereload": "^0.9.1",
-    "mini-css-extract-plugin": "^0.9.0",
-    "node-sass": "^4.13.1",
     "npm-run-all": "^4.1.5",
-    "sass-loader": "^8.0.2",
-    "style-loader": "^1.1.3",
-    "webpack": "^4.42.1",
-    "webpack-cli": "^3.3.11"
+    "punycode": "^2.1.1",
+    "sass": "^1.26.10"
   },
   "dependencies": {
     "clipboard": "^2.0.6",
-    "codemirror": "^5.52.2",
-    "dropzone": "^5.7.0",
-    "markdown-it": "^10.0.0",
+    "codemirror": "^5.57.0",
+    "dropzone": "^5.7.2",
+    "markdown-it": "^11.0.0",
     "markdown-it-task-lists": "^2.1.1",
-    "sortablejs": "^1.10.2",
-    "vue": "^2.6.11",
-    "vuedraggable": "^2.23.2"
-  },
-  "browser": {
-    "vue": "vue/dist/vue.common.js"
+    "sortablejs": "^1.10.2"
   }
 }
index 009791fc9d4cb33ddc88001dec5fef60172e7dc1..ccde28033ab9a3675dbf0203c073e09c01108925 100644 (file)
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -3,6 +3,7 @@
     <description>The coding standard for BookStack.</description>
     <file>app</file>
     <exclude-pattern>*/migrations/*</exclude-pattern>
+    <exclude-pattern>*/tests/*</exclude-pattern>
     <arg value="np"/>
     <rule ref="PSR2"/>
 </ruleset>
\ No newline at end of file
index 85538c446a2438d7df122dd570b5141c64d1dbec..70f1c1f9c3ed341b39af5baa54e79af2de7a70ab 100644 (file)
@@ -51,5 +51,7 @@
         <server name="DEBUGBAR_ENABLED" value="false"/>
         <server name="SAML2_ENABLED" value="false"/>
         <server name="API_REQUESTS_PER_MIN" value="180"/>
+        <server name="LOG_FAILED_LOGIN_MESSAGE" value=""/>
+        <server name="LOG_FAILED_LOGIN_CHANNEL" value="testing"/>
     </php>
 </phpunit>
index 2c68d094c1e5c20c3dc1167072db1c7eb1b2d5e5..0c0626f5b5436ff203c8adca88acaaef7467c352 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -51,7 +51,7 @@ All development on BookStack is currently done on the master branch. When it's t
 
 * [Node.js](https://nodejs.org/en/) v10.0+
 
-This project uses SASS for CSS development and this is built, along with the JavaScript, using webpack. The below npm commands can be used to install the dependencies & run the build tasks:
+This project uses SASS for CSS development and this is built, along with the JavaScript, using a range of npm scripts. The below npm commands can be used to install the dependencies & run the build tasks:
 
 ``` bash
 # Install NPM Dependencies
@@ -157,8 +157,7 @@ These are the great open-source projects used to help build BookStack:
 * [Laravel](http://laravel.com/)
 * [TinyMCE](https://www.tinymce.com/)
 * [CodeMirror](https://codemirror.net)
-* [Vue.js](http://vuejs.org/)
-* [Sortable](https://github.com/SortableJS/Sortable) & [Vue.Draggable](https://github.com/SortableJS/Vue.Draggable)
+* [Sortable](https://github.com/SortableJS/Sortable)
 * [Google Material Icons](https://material.io/icons/)
 * [Dropzone.js](http://www.dropzonejs.com/)
 * [clipboard.js](https://clipboardjs.com/)
diff --git a/resources/js/components/add-remove-rows.js b/resources/js/components/add-remove-rows.js
new file mode 100644 (file)
index 0000000..9a5f019
--- /dev/null
@@ -0,0 +1,54 @@
+import {onChildEvent} from "../services/dom";
+import {uniqueId} from "../services/util";
+
+/**
+ * AddRemoveRows
+ * Allows easy row add/remove controls onto a table.
+ * Needs a model row to use when adding a new row.
+ * @extends {Component}
+ */
+class AddRemoveRows {
+    setup() {
+        this.modelRow = this.$refs.model;
+        this.addButton = this.$refs.add;
+        this.removeSelector = this.$opts.removeSelector;
+        this.rowSelector = this.$opts.rowSelector;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.addButton.addEventListener('click', this.add.bind(this));
+
+        onChildEvent(this.$el, this.removeSelector, 'click', (e) => {
+            const row = e.target.closest(this.rowSelector);
+            row.remove();
+        });
+    }
+
+    // For external use
+    add() {
+        const clone = this.modelRow.cloneNode(true);
+        clone.classList.remove('hidden');
+        this.setClonedInputNames(clone);
+        this.modelRow.parentNode.insertBefore(clone, this.modelRow);
+        window.components.init(clone);
+    }
+
+    /**
+     * Update the HTML names of a clone to be unique if required.
+     * Names can use placeholder values. For exmaple, a model row
+     * may have name="tags[randrowid][name]".
+     * These are the available placeholder values:
+     * - randrowid - An random string ID, applied the same across the row.
+     * @param {HTMLElement} clone
+     */
+    setClonedInputNames(clone) {
+        const rowId = uniqueId();
+        const randRowIdElems = clone.querySelectorAll(`[name*="randrowid"]`);
+        for (const elem of randRowIdElems) {
+            elem.name = elem.name.split('randrowid').join(rowId);
+        }
+    }
+}
+
+export default AddRemoveRows;
\ No newline at end of file
diff --git a/resources/js/components/ajax-delete-row.js b/resources/js/components/ajax-delete-row.js
new file mode 100644 (file)
index 0000000..2feb3d5
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * AjaxDelete
+ * @extends {Component}
+ */
+import {onSelect} from "../services/dom";
+
+class AjaxDeleteRow {
+    setup() {
+        this.row = this.$el;
+        this.url = this.$opts.url;
+        this.deleteButtons = this.$manyRefs.delete;
+
+        onSelect(this.deleteButtons, this.runDelete.bind(this));
+    }
+
+    runDelete() {
+        this.row.style.opacity = '0.7';
+        this.row.style.pointerEvents = 'none';
+
+        window.$http.delete(this.url).then(resp => {
+            if (typeof resp.data === 'object' && resp.data.message) {
+                window.$events.emit('success', resp.data.message);
+            }
+            this.row.remove();
+        }).catch(err => {
+            this.row.style.opacity = null;
+            this.row.style.pointerEvents = null;
+        });
+    }
+}
+
+export default AjaxDeleteRow;
\ No newline at end of file
diff --git a/resources/js/components/ajax-form.js b/resources/js/components/ajax-form.js
new file mode 100644 (file)
index 0000000..91029d0
--- /dev/null
@@ -0,0 +1,82 @@
+import {onEnterPress, onSelect} from "../services/dom";
+
+/**
+ * Ajax Form
+ * Will handle button clicks or input enter press events and submit
+ * the data over ajax. Will always expect a partial HTML view to be returned.
+ * Fires an 'ajax-form-success' event when submitted successfully.
+ *
+ * Will handle a real form if that's what the component is added to
+ * otherwise will act as a fake form element.
+ *
+ * @extends {Component}
+ */
+class AjaxForm {
+    setup() {
+        this.container = this.$el;
+        this.responseContainer = this.container;
+        this.url = this.$opts.url;
+        this.method = this.$opts.method || 'post';
+        this.successMessage = this.$opts.successMessage;
+        this.submitButtons = this.$manyRefs.submit || [];
+
+        if (this.$opts.responseContainer) {
+            this.responseContainer = this.container.closest(this.$opts.responseContainer);
+        }
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+
+        if (this.container.tagName === 'FORM') {
+            this.container.addEventListener('submit', this.submitRealForm.bind(this));
+            return;
+        }
+
+        onEnterPress(this.container, event => {
+            this.submitFakeForm();
+            event.preventDefault();
+        });
+
+        this.submitButtons.forEach(button => onSelect(button, this.submitFakeForm.bind(this)));
+    }
+
+    submitFakeForm() {
+        const fd = new FormData();
+        const inputs = this.container.querySelectorAll(`[name]`);
+        for (const input of inputs) {
+            fd.append(input.getAttribute('name'), input.value);
+        }
+        this.submit(fd);
+    }
+
+    submitRealForm(event) {
+        event.preventDefault();
+        const fd = new FormData(this.container);
+        this.submit(fd);
+    }
+
+    async submit(formData) {
+        this.responseContainer.style.opacity = '0.7';
+        this.responseContainer.style.pointerEvents = 'none';
+
+        try {
+            const resp = await window.$http[this.method.toLowerCase()](this.url, formData);
+            this.$emit('success', {formData});
+            this.responseContainer.innerHTML = resp.data;
+            if (this.successMessage) {
+                window.$events.emit('success', this.successMessage);
+            }
+        } catch (err) {
+            this.responseContainer.innerHTML = err.data;
+        }
+
+        window.components.init(this.responseContainer);
+        this.responseContainer.style.opacity = null;
+        this.responseContainer.style.pointerEvents = null;
+    }
+
+}
+
+export default AjaxForm;
\ No newline at end of file
diff --git a/resources/js/components/attachments.js b/resources/js/components/attachments.js
new file mode 100644 (file)
index 0000000..6dcfe9f
--- /dev/null
@@ -0,0 +1,79 @@
+/**
+ * Attachments
+ * @extends {Component}
+ */
+import {showLoading} from "../services/dom";
+
+class Attachments {
+
+    setup() {
+        this.container = this.$el;
+        this.pageId = this.$opts.pageId;
+        this.editContainer = this.$refs.editContainer;
+        this.listContainer = this.$refs.listContainer;
+        this.mainTabs = this.$refs.mainTabs;
+        this.list = this.$refs.list;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        const reloadListBound = this.reloadList.bind(this);
+        this.container.addEventListener('dropzone-success', reloadListBound);
+        this.container.addEventListener('ajax-form-success', reloadListBound);
+
+        this.container.addEventListener('sortable-list-sort', event => {
+            this.updateOrder(event.detail.ids);
+        });
+
+        this.container.addEventListener('event-emit-select-edit', event => {
+            this.startEdit(event.detail.id);
+        });
+
+        this.container.addEventListener('event-emit-select-edit-back', event => {
+            this.stopEdit();
+        });
+
+        this.container.addEventListener('event-emit-select-insert', event => {
+            const insertContent = event.target.closest('[data-drag-content]').getAttribute('data-drag-content');
+            const contentTypes = JSON.parse(insertContent);
+            window.$events.emit('editor::insert', {
+                html: contentTypes['text/html'],
+                markdown: contentTypes['text/plain'],
+            });
+        });
+    }
+
+    reloadList() {
+        this.stopEdit();
+        this.mainTabs.components.tabs.show('items');
+        window.$http.get(`/attachments/get/page/${this.pageId}`).then(resp => {
+            this.list.innerHTML = resp.data;
+            window.components.init(this.list);
+        });
+    }
+
+    updateOrder(idOrder) {
+        window.$http.put(`/attachments/sort/page/${this.pageId}`, {order: idOrder}).then(resp => {
+            window.$events.emit('success', resp.data.message);
+        });
+    }
+
+    async startEdit(id) {
+        this.editContainer.classList.remove('hidden');
+        this.listContainer.classList.add('hidden');
+
+        showLoading(this.editContainer);
+        const resp = await window.$http.get(`/attachments/edit/${id}`);
+        this.editContainer.innerHTML = resp.data;
+        window.components.init(this.editContainer);
+    }
+
+    stopEdit() {
+        this.editContainer.classList.add('hidden');
+        this.listContainer.classList.remove('hidden');
+    }
+
+}
+
+export default Attachments;
\ No newline at end of file
diff --git a/resources/js/components/auto-suggest.js b/resources/js/components/auto-suggest.js
new file mode 100644 (file)
index 0000000..68de49b
--- /dev/null
@@ -0,0 +1,152 @@
+import {escapeHtml} from "../services/util";
+import {onChildEvent} from "../services/dom";
+
+const ajaxCache = {};
+
+/**
+ * AutoSuggest
+ * @extends {Component}
+ */
+class AutoSuggest {
+    setup() {
+        this.parent = this.$el.parentElement;
+        this.container = this.$el;
+        this.type = this.$opts.type;
+        this.url = this.$opts.url;
+        this.input = this.$refs.input;
+        this.list = this.$refs.list;
+
+        this.lastPopulated = 0;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.input.addEventListener('input', this.requestSuggestions.bind(this));
+        this.input.addEventListener('focus', this.requestSuggestions.bind(this));
+        this.input.addEventListener('keydown', event => {
+            if (event.key === 'Tab') {
+                this.hideSuggestions();
+            }
+        });
+
+        this.input.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
+        this.container.addEventListener('keydown', this.containerKeyDown.bind(this));
+
+        onChildEvent(this.list, 'button', 'click', (event, el) => {
+            this.selectSuggestion(el.textContent);
+        });
+        onChildEvent(this.list, 'button', 'keydown', (event, el) => {
+            if (event.key === 'Enter') {
+                this.selectSuggestion(el.textContent);
+            }
+        });
+
+    }
+
+    selectSuggestion(value) {
+        this.input.value = value;
+        this.lastPopulated = Date.now();
+        this.input.focus();
+        this.input.dispatchEvent(new Event('input', {bubbles: true}));
+        this.input.dispatchEvent(new Event('change', {bubbles: true}));
+        this.hideSuggestions();
+    }
+
+    containerKeyDown(event) {
+        if (event.key === 'Enter') event.preventDefault();
+        if (this.list.classList.contains('hidden')) return;
+
+        // Down arrow
+        if (event.key === 'ArrowDown') {
+            this.moveFocus(true);
+            event.preventDefault();
+        }
+        // Up Arrow
+        else if (event.key === 'ArrowUp') {
+            this.moveFocus(false);
+            event.preventDefault();
+        }
+        // Escape key
+        else if (event.key === 'Escape') {
+            this.hideSuggestions();
+            event.preventDefault();
+        }
+    }
+
+    moveFocus(forward = true) {
+        const focusables = Array.from(this.container.querySelectorAll('input,button'));
+        const index = focusables.indexOf(document.activeElement);
+        const newFocus = focusables[index + (forward ? 1 : -1)];
+        if (newFocus) {
+            newFocus.focus()
+        }
+    }
+
+    async requestSuggestions() {
+        if (Date.now() - this.lastPopulated < 50) {
+            return;
+        }
+
+        const nameFilter = this.getNameFilterIfNeeded();
+        const search = this.input.value.slice(0, 3).toLowerCase();
+        const suggestions = await this.loadSuggestions(search, nameFilter);
+        let toShow = suggestions.slice(0, 6);
+        if (search.length > 0) {
+            toShow = suggestions.filter(val => {
+                return val.toLowerCase().includes(search);
+            }).slice(0, 6);
+        }
+
+        this.displaySuggestions(toShow);
+    }
+
+    getNameFilterIfNeeded() {
+        if (this.type !== 'value') return null;
+        return this.parent.querySelector('input').value;
+    }
+
+    /**
+     * @param {String} search
+     * @param {String|null} nameFilter
+     * @returns {Promise<Object|String|*>}
+     */
+    async loadSuggestions(search, nameFilter = null) {
+        const params = {search, name: nameFilter};
+        const cacheKey = `${this.url}:${JSON.stringify(params)}`;
+
+        if (ajaxCache[cacheKey]) {
+            return ajaxCache[cacheKey];
+        }
+
+        const resp = await window.$http.get(this.url, params);
+        ajaxCache[cacheKey] = resp.data;
+        return resp.data;
+    }
+
+    /**
+     * @param {String[]} suggestions
+     */
+    displaySuggestions(suggestions) {
+        if (suggestions.length === 0) {
+            return this.hideSuggestions();
+        }
+
+        this.list.innerHTML = suggestions.map(value => `<li><button type="button">${escapeHtml(value)}</button></li>`).join('');
+        this.list.style.display = 'block';
+        for (const button of this.list.querySelectorAll('button')) {
+            button.addEventListener('blur', this.hideSuggestionsIfFocusedLost.bind(this));
+        }
+    }
+
+    hideSuggestions() {
+        this.list.style.display = 'none';
+    }
+
+    hideSuggestionsIfFocusedLost(event) {
+        if (!this.container.contains(event.relatedTarget)) {
+            this.hideSuggestions();
+        }
+    }
+}
+
+export default AutoSuggest;
\ No newline at end of file
diff --git a/resources/js/components/code-editor.js b/resources/js/components/code-editor.js
new file mode 100644 (file)
index 0000000..2e3506e
--- /dev/null
@@ -0,0 +1,117 @@
+import Code from "../services/code";
+import {onChildEvent, onEnterPress, onSelect} from "../services/dom";
+
+/**
+ * Code Editor
+ * @extends {Component}
+ */
+class CodeEditor {
+
+    setup() {
+        this.container = this.$refs.container;
+        this.popup = this.$el;
+        this.editorInput = this.$refs.editor;
+        this.languageLinks = this.$manyRefs.languageLink;
+        this.saveButton = this.$refs.saveButton;
+        this.languageInput = this.$refs.languageInput;
+        this.historyDropDown = this.$refs.historyDropDown;
+        this.historyList = this.$refs.historyList;
+
+        this.callback = null;
+        this.editor = null;
+        this.history = {};
+        this.historyKey = 'code_history';
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.container.addEventListener('keydown', event => {
+            if (event.ctrlKey && event.key === 'Enter') {
+                this.save();
+            }
+        });
+
+        onSelect(this.languageLinks, event => {
+            const language = event.target.dataset.lang;
+            this.languageInput.value = language;
+            this.updateEditorMode(language);
+        });
+
+        onEnterPress(this.languageInput, e => this.save());
+        onSelect(this.saveButton, e => this.save());
+
+        onChildEvent(this.historyList, 'button', 'click', (event, elem) => {
+            event.preventDefault();
+            const historyTime = elem.dataset.time;
+            if (this.editor) {
+                this.editor.setValue(this.history[historyTime]);
+            }
+        });
+    }
+
+    save() {
+        if (this.callback) {
+            this.callback(this.editor.getValue(), this.languageInput.value);
+        }
+        this.hide();
+    }
+
+    open(code, language, callback) {
+        this.languageInput.value = language;
+        this.callback = callback;
+
+        this.show();
+        this.updateEditorMode(language);
+
+        Code.setContent(this.editor, code);
+    }
+
+    show() {
+        if (!this.editor) {
+            this.editor = Code.popupEditor(this.editorInput, this.languageInput.value);
+        }
+        this.loadHistory();
+        this.popup.components.popup.show(() => {
+            Code.updateLayout(this.editor);
+            this.editor.focus();
+        }, () => {
+            this.addHistory()
+        });
+    }
+
+    hide() {
+        this.popup.components.popup.hide();
+        this.addHistory();
+    }
+
+    updateEditorMode(language) {
+        Code.setMode(this.editor, language, this.editor.getValue());
+    }
+
+    loadHistory() {
+        this.history = JSON.parse(window.sessionStorage.getItem(this.historyKey) || '{}');
+        const historyKeys = Object.keys(this.history).reverse();
+        this.historyDropDown.classList.toggle('hidden', historyKeys.length === 0);
+        this.historyList.innerHTML = historyKeys.map(key => {
+             const localTime = (new Date(parseInt(key))).toLocaleTimeString();
+             return `<li><button type="button" data-time="${key}">${localTime}</button></li>`;
+        }).join('');
+    }
+
+    addHistory() {
+        if (!this.editor) return;
+        const code = this.editor.getValue();
+        if (!code) return;
+
+        // Stop if we'd be storing the same as the last item
+        const lastHistoryKey = Object.keys(this.history).pop();
+        if (this.history[lastHistoryKey] === code) return;
+
+        this.history[String(Date.now())] = code;
+        const historyString = JSON.stringify(this.history);
+        window.sessionStorage.setItem(this.historyKey, historyString);
+    }
+
+}
+
+export default CodeEditor;
\ No newline at end of file
index a630f38f2a577591916f2e86796d8b796f4634a1..544f91008c7d13eaeec88a9f119ebfb47e839a85 100644 (file)
@@ -37,7 +37,7 @@ class Collapsible {
     }
 
     openIfContainsError() {
-        const error = this.content.querySelector('.text-neg');
+        const error = this.content.querySelector('.text-neg.text-small');
         if (error) {
             this.open();
         }
index 367c956ce3b7b757c28cefc6140c1ea19c794d35..7b1ce30556d41bde39b961065b47f87dcc5783f4 100644 (file)
@@ -3,14 +3,16 @@ import {onSelect} from "../services/dom";
 /**
  * Dropdown
  * Provides some simple logic to create simple dropdown menus.
+ * @extends {Component}
  */
 class DropDown {
 
-    constructor(elem) {
-        this.container = elem;
-        this.menu = elem.querySelector('.dropdown-menu, [dropdown-menu]');
-        this.moveMenu = elem.hasAttribute('dropdown-move-menu');
-        this.toggle = elem.querySelector('[dropdown-toggle]');
+    setup() {
+        this.container = this.$el;
+        this.menu = this.$refs.menu;
+        this.toggle = this.$refs.toggle;
+        this.moveMenu = this.$opts.moveMenu;
+
         this.direction = (document.dir === 'rtl') ? 'right' : 'left';
         this.body = document.body;
         this.showing = false;
diff --git a/resources/js/components/dropzone.js b/resources/js/components/dropzone.js
new file mode 100644 (file)
index 0000000..e7273df
--- /dev/null
@@ -0,0 +1,77 @@
+import DropZoneLib from "dropzone";
+import {fadeOut} from "../services/animations";
+
+/**
+ * Dropzone
+ * @extends {Component}
+ */
+class Dropzone {
+    setup() {
+        this.container = this.$el;
+        this.url = this.$opts.url;
+        this.successMessage = this.$opts.successMessage;
+        this.removeMessage = this.$opts.removeMessage;
+        this.uploadLimitMessage = this.$opts.uploadLimitMessage;
+        this.timeoutMessage = this.$opts.timeoutMessage;
+
+        const _this = this;
+        this.dz = new DropZoneLib(this.container, {
+            addRemoveLinks: true,
+            dictRemoveFile: this.removeMessage,
+            timeout: Number(window.uploadTimeout) || 60000,
+            maxFilesize: Number(window.uploadLimit) || 256,
+            url: this.url,
+            withCredentials: true,
+            init() {
+                this.dz = this;
+                this.dz.on('sending', _this.onSending.bind(_this));
+                this.dz.on('success', _this.onSuccess.bind(_this));
+                this.dz.on('error', _this.onError.bind(_this));
+            }
+        });
+    }
+
+    onSending(file, xhr, data) {
+
+        const token = window.document.querySelector('meta[name=token]').getAttribute('content');
+        data.append('_token', token);
+
+        xhr.ontimeout = (e) => {
+            this.dz.emit('complete', file);
+            this.dz.emit('error', file, this.timeoutMessage);
+        }
+    }
+
+    onSuccess(file, data) {
+        this.$emit('success', {file, data});
+
+        if (this.successMessage) {
+            window.$events.emit('success', this.successMessage);
+        }
+
+        fadeOut(file.previewElement, 800, () => {
+            this.dz.removeFile(file);
+        });
+    }
+
+    onError(file, errorMessage, xhr) {
+        this.$emit('error', {file, errorMessage, xhr});
+
+        const setMessage = (message) => {
+            const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
+            messsageEl.textContent = message;
+        }
+
+        if (xhr && xhr.status === 413) {
+            setMessage(this.uploadLimitMessage);
+        } else if (errorMessage.file) {
+            setMessage(errorMessage.file);
+        }
+    }
+
+    removeAll() {
+        this.dz.removeAllFiles(true);
+    }
+}
+
+export default Dropzone;
\ No newline at end of file
diff --git a/resources/js/components/entity-search.js b/resources/js/components/entity-search.js
new file mode 100644 (file)
index 0000000..8b1861e
--- /dev/null
@@ -0,0 +1,59 @@
+import {onSelect} from "../services/dom";
+
+/**
+ * Class EntitySearch
+ * @extends {Component}
+ */
+class EntitySearch {
+    setup() {
+        this.entityId = this.$opts.entityId;
+        this.entityType = this.$opts.entityType;
+
+        this.contentView = this.$refs.contentView;
+        this.searchView = this.$refs.searchView;
+        this.searchResults = this.$refs.searchResults;
+        this.searchInput = this.$refs.searchInput;
+        this.searchForm = this.$refs.searchForm;
+        this.clearButton = this.$refs.clearButton;
+        this.loadingBlock = this.$refs.loadingBlock;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.searchInput.addEventListener('change', this.runSearch.bind(this));
+        this.searchForm.addEventListener('submit', e => {
+            e.preventDefault();
+            this.runSearch();
+        });
+
+        onSelect(this.clearButton, this.clearSearch.bind(this));
+    }
+
+    runSearch() {
+        const term = this.searchInput.value.trim();
+        if (term.length === 0) {
+            return this.clearSearch();
+        }
+
+        this.searchView.classList.remove('hidden');
+        this.contentView.classList.add('hidden');
+        this.loadingBlock.classList.remove('hidden');
+
+        const url = window.baseUrl(`/search/${this.entityType}/${this.entityId}`);
+        window.$http.get(url, {term}).then(resp => {
+            this.searchResults.innerHTML = resp.data;
+        }).catch(console.error).then(() => {
+            this.loadingBlock.classList.add('hidden');
+        });
+    }
+
+    clearSearch() {
+        this.searchView.classList.add('hidden');
+        this.contentView.classList.remove('hidden');
+        this.loadingBlock.classList.add('hidden');
+        this.searchInput.value = '';
+    }
+}
+
+export default EntitySearch;
\ No newline at end of file
index 147f7b58353785e55995a1fef5af134a49cf5ff3..0104eace7065373983792a525c501746616bc2c9 100644 (file)
@@ -1,27 +1,29 @@
-
+/**
+ * Entity Selector Popup
+ * @extends {Component}
+ */
 class EntitySelectorPopup {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
+        this.selectButton = this.$refs.select;
         window.EntitySelectorPopup = this;
 
         this.callback = null;
         this.selection = null;
 
-        this.selectButton = elem.querySelector('.entity-link-selector-confirm');
         this.selectButton.addEventListener('click', this.onSelectButtonClick.bind(this));
-
         window.$events.listen('entity-select-change', this.onSelectionChange.bind(this));
         window.$events.listen('entity-select-confirm', this.onSelectionConfirm.bind(this));
     }
 
     show(callback) {
         this.callback = callback;
-        this.elem.components.overlay.show();
+        this.elem.components.popup.show();
     }
 
     hide() {
-        this.elem.components.overlay.hide();
+        this.elem.components.popup.hide();
     }
 
     onSelectButtonClick() {
diff --git a/resources/js/components/event-emit-select.js b/resources/js/components/event-emit-select.js
new file mode 100644 (file)
index 0000000..cf02158
--- /dev/null
@@ -0,0 +1,29 @@
+import {onSelect} from "../services/dom";
+
+/**
+ * EventEmitSelect
+ * Component will simply emit an event when selected.
+ *
+ * Has one required option: "name".
+ * A name of "hello" will emit a component DOM event of
+ * "event-emit-select-name"
+ *
+ * All options will be set as the "detail" of the event with
+ * their values included.
+ *
+ * @extends {Component}
+ */
+class EventEmitSelect {
+    setup() {
+        this.container = this.$el;
+        this.name = this.$opts.name;
+
+
+        onSelect(this.$el, () => {
+            this.$emit(this.name, this.$opts);
+        });
+    }
+
+}
+
+export default EventEmitSelect;
\ No newline at end of file
diff --git a/resources/js/components/image-manager.js b/resources/js/components/image-manager.js
new file mode 100644 (file)
index 0000000..c974ab1
--- /dev/null
@@ -0,0 +1,208 @@
+import {onChildEvent, onSelect, removeLoading, showLoading} from "../services/dom";
+
+/**
+ * ImageManager
+ * @extends {Component}
+ */
+class ImageManager {
+
+    setup() {
+
+        // Options
+        this.uploadedTo = this.$opts.uploadedTo;
+
+        // Element References
+        this.container = this.$el;
+        this.popupEl = this.$refs.popup;
+        this.searchForm = this.$refs.searchForm;
+        this.searchInput = this.$refs.searchInput;
+        this.cancelSearch = this.$refs.cancelSearch;
+        this.listContainer = this.$refs.listContainer;
+        this.filterTabs = this.$manyRefs.filterTabs;
+        this.selectButton = this.$refs.selectButton;
+        this.formContainer = this.$refs.formContainer;
+        this.dropzoneContainer = this.$refs.dropzoneContainer;
+
+        // Instance data
+        this.type = 'gallery';
+        this.lastSelected = {};
+        this.lastSelectedTime = 0;
+        this.callback = null;
+        this.resetState = () => {
+            this.hasData = false;
+            this.page = 1;
+            this.filter = 'all';
+        };
+        this.resetState();
+
+        this.setupListeners();
+
+        window.ImageManager = this;
+    }
+
+    setupListeners() {
+        onSelect(this.filterTabs, e => {
+            this.resetAll();
+            this.filter = e.target.dataset.filter;
+            this.setActiveFilterTab(this.filter);
+            this.loadGallery();
+        });
+
+        this.searchForm.addEventListener('submit', event => {
+            this.resetListView();
+            this.loadGallery();
+            event.preventDefault();
+        });
+
+        onSelect(this.cancelSearch, event => {
+            this.resetListView();
+            this.resetSearchView();
+            this.loadGallery();
+            this.cancelSearch.classList.remove('active');
+        });
+
+        this.searchInput.addEventListener('input', event => {
+            this.cancelSearch.classList.toggle('active', this.searchInput.value.trim());
+        });
+
+        onChildEvent(this.listContainer, '.load-more', 'click', async event => {
+            showLoading(event.target);
+            this.page++;
+            await this.loadGallery();
+            event.target.remove();
+        });
+
+        this.listContainer.addEventListener('event-emit-select-image', this.onImageSelectEvent.bind(this));
+
+        onSelect(this.selectButton, () => {
+            if (this.callback) {
+                this.callback(this.lastSelected);
+            }
+            this.hide();
+        });
+
+        onChildEvent(this.formContainer, '#image-manager-delete', 'click', event => {
+            if (this.lastSelected) {
+                this.loadImageEditForm(this.lastSelected.id, true);
+            }
+        });
+
+        this.formContainer.addEventListener('ajax-form-success', this.refreshGallery.bind(this));
+        this.container.addEventListener('dropzone-success', this.refreshGallery.bind(this));
+    }
+
+    show(callback, type = 'gallery') {
+        this.resetAll();
+
+        this.callback = callback;
+        this.type = type;
+        this.popupEl.components.popup.show();
+        this.dropzoneContainer.classList.toggle('hidden', type !== 'gallery');
+
+        if (!this.hasData) {
+            this.loadGallery();
+            this.hasData = true;
+        }
+    }
+
+    hide() {
+        this.popupEl.components.popup.hide();
+    }
+
+    async loadGallery() {
+        const params = {
+            page: this.page,
+            search: this.searchInput.value || null,
+            uploaded_to: this.uploadedTo,
+            filter_type: this.filter === 'all' ? null : this.filter,
+        };
+
+        const {data: html} = await window.$http.get(`images/${this.type}`, params);
+        this.addReturnedHtmlElementsToList(html);
+        removeLoading(this.listContainer);
+    }
+
+    addReturnedHtmlElementsToList(html) {
+        const el = document.createElement('div');
+        el.innerHTML = html;
+        window.components.init(el);
+        for (const child of [...el.children]) {
+            this.listContainer.appendChild(child);
+        }
+    }
+
+    setActiveFilterTab(filterName) {
+        this.filterTabs.forEach(t => t.classList.remove('selected'));
+        const activeTab = this.filterTabs.find(t => t.dataset.filter === filterName);
+        if (activeTab) {
+            activeTab.classList.add('selected');
+        }
+    }
+
+    resetAll() {
+        this.resetState();
+        this.resetListView();
+        this.resetSearchView();
+        this.resetEditForm();
+        this.setActiveFilterTab('all');
+        this.selectButton.classList.add('hidden');
+    }
+
+    resetSearchView() {
+        this.searchInput.value = '';
+    }
+
+    resetEditForm() {
+        this.formContainer.innerHTML = '';
+    }
+
+    resetListView() {
+        showLoading(this.listContainer);
+        this.page = 1;
+    }
+
+    refreshGallery() {
+        this.resetListView();
+        this.loadGallery();
+    }
+
+    onImageSelectEvent(event) {
+        const image = JSON.parse(event.detail.data);
+        const isDblClick = ((image && image.id === this.lastSelected.id)
+            && Date.now() - this.lastSelectedTime < 400);
+        const alreadySelected = event.target.classList.contains('selected');
+        [...this.listContainer.querySelectorAll('.selected')].forEach(el => {
+            el.classList.remove('selected');
+        });
+
+        if (!alreadySelected) {
+            event.target.classList.add('selected');
+            this.loadImageEditForm(image.id);
+        } else {
+            this.resetEditForm();
+        }
+        this.selectButton.classList.toggle('hidden', alreadySelected);
+
+        if (isDblClick && this.callback) {
+            this.callback(image);
+            this.hide();
+        }
+
+        this.lastSelected = image;
+        this.lastSelectedTime = Date.now();
+    }
+
+    async loadImageEditForm(imageId, requestDelete = false) {
+        if (!requestDelete) {
+            this.formContainer.innerHTML = '';
+        }
+
+        const params = requestDelete ? {delete: true} : {};
+        const {data: formHtml} = await window.$http.get(`/images/edit/${imageId}`, params);
+        this.formContainer.innerHTML = formHtml;
+        window.components.init(this.formContainer);
+    }
+
+}
+
+export default ImageManager;
\ No newline at end of file
index 11282733090645850ad2f4c487ca21c6955a3d25..87c496c91c591718a269b663d3ac329230a778ab 100644 (file)
-import dropdown from "./dropdown";
-import overlay from "./overlay";
-import backToTop from "./back-to-top";
-import notification from "./notification";
-import chapterToggle from "./chapter-toggle";
-import expandToggle from "./expand-toggle";
-import entitySelectorPopup from "./entity-selector-popup";
-import entitySelector from "./entity-selector";
-import sidebar from "./sidebar";
-import pagePicker from "./page-picker";
-import pageComments from "./page-comments";
-import wysiwygEditor from "./wysiwyg-editor";
-import markdownEditor from "./markdown-editor";
-import editorToolbox from "./editor-toolbox";
-import imagePicker from "./image-picker";
-import collapsible from "./collapsible";
-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";
-import customCheckbox from "./custom-checkbox";
-import bookSort from "./book-sort";
-import settingAppColorPicker from "./setting-app-color-picker";
-import settingColorPicker from "./setting-color-picker";
-import entityPermissionsEditor from "./entity-permissions-editor";
-import templateManager from "./template-manager";
-import newUserPassword from "./new-user-password";
-import detailsHighlighter from "./details-highlighter";
-import codeHighlighter from "./code-highlighter";
+import addRemoveRows from "./add-remove-rows.js"
+import ajaxDeleteRow from "./ajax-delete-row.js"
+import ajaxForm from "./ajax-form.js"
+import attachments from "./attachments.js"
+import autoSuggest from "./auto-suggest.js"
+import backToTop from "./back-to-top.js"
+import bookSort from "./book-sort.js"
+import breadcrumbListing from "./breadcrumb-listing.js"
+import chapterToggle from "./chapter-toggle.js"
+import codeEditor from "./code-editor.js"
+import codeHighlighter from "./code-highlighter.js"
+import collapsible from "./collapsible.js"
+import customCheckbox from "./custom-checkbox.js"
+import detailsHighlighter from "./details-highlighter.js"
+import dropdown from "./dropdown.js"
+import dropzone from "./dropzone.js"
+import editorToolbox from "./editor-toolbox.js"
+import entityPermissionsEditor from "./entity-permissions-editor.js"
+import entitySearch from "./entity-search.js"
+import entitySelector from "./entity-selector.js"
+import entitySelectorPopup from "./entity-selector-popup.js"
+import eventEmitSelect from "./event-emit-select.js"
+import expandToggle from "./expand-toggle.js"
+import headerMobileToggle from "./header-mobile-toggle.js"
+import homepageControl from "./homepage-control.js"
+import imageManager from "./image-manager.js"
+import imagePicker from "./image-picker.js"
+import index from "./index.js"
+import listSortControl from "./list-sort-control.js"
+import markdownEditor from "./markdown-editor.js"
+import newUserPassword from "./new-user-password.js"
+import notification from "./notification.js"
+import optionalInput from "./optional-input.js"
+import pageComments from "./page-comments.js"
+import pageDisplay from "./page-display.js"
+import pageEditor from "./page-editor.js"
+import pagePicker from "./page-picker.js"
+import permissionsTable from "./permissions-table.js"
+import popup from "./popup.js"
+import settingAppColorPicker from "./setting-app-color-picker.js"
+import settingColorPicker from "./setting-color-picker.js"
+import shelfSort from "./shelf-sort.js"
+import sidebar from "./sidebar.js"
+import sortableList from "./sortable-list.js"
+import submitOnChange from "./submit-on-change.js"
+import tabs from "./tabs.js"
+import tagManager from "./tag-manager.js"
+import templateManager from "./template-manager.js"
+import toggleSwitch from "./toggle-switch.js"
+import triLayout from "./tri-layout.js"
+import wysiwygEditor from "./wysiwyg-editor.js"
 
 const componentMapping = {
-    'dropdown': dropdown,
-    'overlay': overlay,
-    'back-to-top': backToTop,
-    'notification': notification,
-    'chapter-toggle': chapterToggle,
-    'expand-toggle': expandToggle,
-    'entity-selector-popup': entitySelectorPopup,
-    'entity-selector': entitySelector,
-    'sidebar': sidebar,
-    'page-picker': pagePicker,
-    'page-comments': pageComments,
-    'wysiwyg-editor': wysiwygEditor,
-    'markdown-editor': markdownEditor,
-    'editor-toolbox': editorToolbox,
-    'image-picker': imagePicker,
-    'collapsible': collapsible,
-    'toggle-switch': toggleSwitch,
-    '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,
-    'custom-checkbox': customCheckbox,
-    'book-sort': bookSort,
-    'setting-app-color-picker': settingAppColorPicker,
-    'setting-color-picker': settingColorPicker,
-    'entity-permissions-editor': entityPermissionsEditor,
-    'template-manager': templateManager,
-    'new-user-password': newUserPassword,
-    'details-highlighter': detailsHighlighter,
-    'code-highlighter': codeHighlighter,
+    "add-remove-rows": addRemoveRows,
+    "ajax-delete-row": ajaxDeleteRow,
+    "ajax-form": ajaxForm,
+    "attachments": attachments,
+    "auto-suggest": autoSuggest,
+    "back-to-top": backToTop,
+    "book-sort": bookSort,
+    "breadcrumb-listing": breadcrumbListing,
+    "chapter-toggle": chapterToggle,
+    "code-editor": codeEditor,
+    "code-highlighter": codeHighlighter,
+    "collapsible": collapsible,
+    "custom-checkbox": customCheckbox,
+    "details-highlighter": detailsHighlighter,
+    "dropdown": dropdown,
+    "dropzone": dropzone,
+    "editor-toolbox": editorToolbox,
+    "entity-permissions-editor": entityPermissionsEditor,
+    "entity-search": entitySearch,
+    "entity-selector": entitySelector,
+    "entity-selector-popup": entitySelectorPopup,
+    "event-emit-select": eventEmitSelect,
+    "expand-toggle": expandToggle,
+    "header-mobile-toggle": headerMobileToggle,
+    "homepage-control": homepageControl,
+    "image-manager": imageManager,
+    "image-picker": imagePicker,
+    "index": index,
+    "list-sort-control": listSortControl,
+    "markdown-editor": markdownEditor,
+    "new-user-password": newUserPassword,
+    "notification": notification,
+    "optional-input": optionalInput,
+    "page-comments": pageComments,
+    "page-display": pageDisplay,
+    "page-editor": pageEditor,
+    "page-picker": pagePicker,
+    "permissions-table": permissionsTable,
+    "popup": popup,
+    "setting-app-color-picker": settingAppColorPicker,
+    "setting-color-picker": settingColorPicker,
+    "shelf-sort": shelfSort,
+    "sidebar": sidebar,
+    "sortable-list": sortableList,
+    "submit-on-change": submitOnChange,
+    "tabs": tabs,
+    "tag-manager": tagManager,
+    "template-manager": templateManager,
+    "toggle-switch": toggleSwitch,
+    "tri-layout": triLayout,
+    "wysiwyg-editor": wysiwygEditor,
 };
 
 window.components = {};
 
-const componentNames = Object.keys(componentMapping);
-
 /**
  * Initialize components of the given name within the given element.
  * @param {String} componentName
  * @param {HTMLElement|Document} parentElement
  */
-function initComponent(componentName, parentElement) {
-    let elems = parentElement.querySelectorAll(`[${componentName}]`);
-    if (elems.length === 0) return;
-
-    let component = componentMapping[componentName];
-    if (typeof window.components[componentName] === "undefined") window.components[componentName] = [];
+function searchForComponentInParent(componentName, parentElement) {
+    const elems = parentElement.querySelectorAll(`[${componentName}]`);
     for (let j = 0, jLen = elems.length; j < jLen; j++) {
-        let instance = new component(elems[j]);
-        if (typeof elems[j].components === 'undefined') elems[j].components = {};
-        elems[j].components[componentName] = instance;
-        window.components[componentName].push(instance);
+        initComponent(componentName, elems[j]);
+    }
+}
+
+/**
+ * Initialize a component instance on the given dom element.
+ * @param {String} name
+ * @param {Element} element
+ */
+function initComponent(name, element) {
+    const componentModel = componentMapping[name];
+    if (componentModel === undefined) return;
+
+    // Create our component instance
+    let instance;
+    try {
+        instance = new componentModel(element);
+        instance.$el = element;
+        const allRefs = parseRefs(name, element);
+        instance.$refs = allRefs.refs;
+        instance.$manyRefs = allRefs.manyRefs;
+        instance.$opts = parseOpts(name, element);
+        instance.$emit = (eventName, data = {}) => {
+            data.from = instance;
+            const event = new CustomEvent(`${name}-${eventName}`, {
+                bubbles: true,
+                detail: data
+            });
+            instance.$el.dispatchEvent(event);
+        };
+        if (typeof instance.setup === 'function') {
+            instance.setup();
+        }
+    } catch (e) {
+        console.error('Failed to create component', e, name, element);
+    }
+
+
+    // Add to global listing
+    if (typeof window.components[name] === "undefined") {
+        window.components[name] = [];
+    }
+    window.components[name].push(instance);
+
+    // Add to element listing
+    if (typeof element.components === 'undefined') {
+        element.components = {};
+    }
+    element.components[name] = instance;
+}
+
+/**
+ * Parse out the element references within the given element
+ * for the given component name.
+ * @param {String} name
+ * @param {Element} element
+ */
+function parseRefs(name, element) {
+    const refs = {};
+    const manyRefs = {};
+
+    const prefix = `${name}@`
+    const selector = `[refs*="${prefix}"]`;
+    const refElems = [...element.querySelectorAll(selector)];
+    if (element.matches(selector)) {
+        refElems.push(element);
+    }
+
+    for (const el of refElems) {
+        const refNames = el.getAttribute('refs')
+            .split(' ')
+            .filter(str => str.startsWith(prefix))
+            .map(str => str.replace(prefix, ''))
+            .map(kebabToCamel);
+        for (const ref of refNames) {
+            refs[ref] = el;
+            if (typeof manyRefs[ref] === 'undefined') {
+                manyRefs[ref] = [];
+            }
+            manyRefs[ref].push(el);
+        }
     }
+    return {refs, manyRefs};
+}
+
+/**
+ * Parse out the element component options.
+ * @param {String} name
+ * @param {Element} element
+ * @return {Object<String, String>}
+ */
+function parseOpts(name, element) {
+    const opts = {};
+    const prefix = `option:${name}:`;
+    for (const {name, value} of element.attributes) {
+        if (name.startsWith(prefix)) {
+            const optName = name.replace(prefix, '');
+            opts[kebabToCamel(optName)] = value || '';
+        }
+    }
+    return opts;
+}
+
+/**
+ * Convert a kebab-case string to camelCase
+ * @param {String} kebab
+ * @returns {string}
+ */
+function kebabToCamel(kebab) {
+    const ucFirst = (word) => word.slice(0,1).toUpperCase() + word.slice(1);
+    const words = kebab.split('-');
+    return words[0] + words.slice(1).map(ucFirst).join('');
 }
 
 /**
@@ -99,11 +234,33 @@ function initComponent(componentName, parentElement) {
  */
 function initAll(parentElement) {
     if (typeof parentElement === 'undefined') parentElement = document;
-    for (let i = 0, len = componentNames.length; i < len; i++) {
-        initComponent(componentNames[i], parentElement);
+
+    // Old attribute system
+    for (const componentName of Object.keys(componentMapping)) {
+        searchForComponentInParent(componentName, parentElement);
+    }
+
+    // New component system
+    const componentElems = parentElement.querySelectorAll(`[component],[components]`);
+
+    for (const el of componentElems) {
+        const componentNames = `${el.getAttribute('component') || ''} ${(el.getAttribute('components'))}`.toLowerCase().split(' ').filter(Boolean);
+        for (const name of componentNames) {
+            initComponent(name, el);
+        }
     }
 }
 
 window.components.init = initAll;
+window.components.first = (name) => (window.components[name] || [null])[0];
 
 export default initAll;
+
+/**
+ * @typedef Component
+ * @property {HTMLElement} $el
+ * @property {Object<String, HTMLElement>} $refs
+ * @property {Object<String, HTMLElement[]>} $manyRefs
+ * @property {Object<String, String>} $opts
+ * @property {function(string, Object)} $emit
+ */
\ No newline at end of file
index cc9a7b859ddb9eeb6f540d1b1c545dd8f1820987..c371a983991dfa333429b824122d0758628a4599 100644 (file)
@@ -8,12 +8,11 @@ import DrawIO from "../services/drawio";
 
 class MarkdownEditor {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.elem = this.$el;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
 
         this.markdown = new MarkdownIt({html: true});
         this.markdown.use(mdTasksLists, {label: true});
@@ -27,12 +26,18 @@ class MarkdownEditor {
 
         this.onMarkdownScroll = this.onMarkdownScroll.bind(this);
 
-        this.display.addEventListener('load', () => {
+        const displayLoad = () => {
             this.displayDoc = this.display.contentDocument;
             this.init();
-        });
+        };
+
+        if (this.display.contentDocument.readyState === 'complete') {
+            displayLoad();
+        } else {
+            this.display.addEventListener('load', displayLoad.bind(this));
+        }
 
-        window.$events.emitPublic(elem, 'editor-markdown::setup', {
+        window.$events.emitPublic(this.elem, 'editor-markdown::setup', {
             markdownIt: this.markdown,
             displayEl: this.display,
             codeMirrorInstance: this.cm,
@@ -251,7 +256,7 @@ class MarkdownEditor {
             }
 
             const clipboard = new Clipboard(event.dataTransfer);
-            if (clipboard.hasItems()) {
+            if (clipboard.hasItems() && clipboard.getImages().length > 0) {
                 const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY});
                 cm.setCursor(cursorPos);
                 event.stopPropagation();
@@ -558,6 +563,12 @@ class MarkdownEditor {
             this.cm.setCursor(cursorPos.line + prependLineCount, cursorPos.ch);
         });
 
+        // Insert editor content at the current location
+        window.$events.listen('editor::insert', (eventContent) => {
+            const markdown = getContentToInsert(eventContent);
+            this.cm.replaceSelection(markdown);
+        });
+
         // Focus on editor
         window.$events.listen('editor::focus', () => {
             this.cm.focus();
diff --git a/resources/js/components/optional-input.js b/resources/js/components/optional-input.js
new file mode 100644 (file)
index 0000000..eab58e4
--- /dev/null
@@ -0,0 +1,28 @@
+import {onSelect} from "../services/dom";
+
+class OptionalInput {
+    setup() {
+        this.removeButton = this.$refs.remove;
+        this.showButton = this.$refs.show;
+        this.input = this.$refs.input;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        onSelect(this.removeButton, () => {
+            this.input.value = '';
+            this.input.classList.add('hidden');
+            this.removeButton.classList.add('hidden');
+            this.showButton.classList.remove('hidden');
+        });
+
+        onSelect(this.showButton, () => {
+            this.input.classList.remove('hidden');
+            this.removeButton.classList.remove('hidden');
+            this.showButton.classList.add('hidden');
+        });
+    }
+
+}
+
+export default OptionalInput;
\ No newline at end of file
diff --git a/resources/js/components/overlay.js b/resources/js/components/overlay.js
deleted file mode 100644 (file)
index 6963ba9..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-import {fadeIn, fadeOut} from "../services/animations";
-
-class Overlay {
-
-    constructor(elem) {
-        this.container = elem;
-        elem.addEventListener('click', event => {
-             if (event.target === elem) return this.hide();
-        });
-
-        window.addEventListener('keyup', event => {
-            if (event.key === 'Escape') {
-                this.hide();
-            }
-        });
-
-        let closeButtons = elem.querySelectorAll('.popup-header-close');
-        for (let i=0; i < closeButtons.length; i++) {
-            closeButtons[i].addEventListener('click', this.hide.bind(this));
-        }
-    }
-
-    hide(onComplete = null) { this.toggle(false, onComplete); }
-    show(onComplete = null) { this.toggle(true, onComplete); }
-
-    toggle(show = true, onComplete) {
-        if (show) {
-            fadeIn(this.container, 240, onComplete);
-        } else {
-            fadeOut(this.container, 240, onComplete);
-        }
-    }
-
-    focusOnBody() {
-        const body = this.container.querySelector('.popup-body');
-        if (body) {
-            body.focus();
-        }
-    }
-
-}
-
-export default Overlay;
\ No newline at end of file
index 5d826cba13d1bb12c0e95e4ba2020db66d487985..c86eead1b865bd8bdaa8184f44bd4ab55a961d7b 100644 (file)
@@ -1,16 +1,31 @@
 import {scrollAndHighlightElement} from "../services/util";
 
+/**
+ * @extends {Component}
+ */
 class PageComments {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.pageId = Number(elem.getAttribute('page-id'));
+    setup() {
+        this.elem = this.$el;
+        this.pageId = Number(this.$opts.pageId);
+
+        // Element references
+        this.container = this.$refs.commentContainer;
+        this.formContainer = this.$refs.formContainer;
+        this.commentCountBar = this.$refs.commentCountBar;
+        this.addButtonContainer = this.$refs.addButtonContainer;
+        this.replyToRow = this.$refs.replyToRow;
+
+        // Translations
+        this.updatedText = this.$opts.updatedText;
+        this.deletedText = this.$opts.deletedText;
+        this.createdText = this.$opts.createdText;
+        this.countText = this.$opts.countText;
+
+        // Internal State
         this.editingComment = null;
         this.parentId = null;
 
-        this.container = elem.querySelector('[comment-container]');
-        this.formContainer = elem.querySelector('[comment-form-container]');
-
         if (this.formContainer) {
             this.form = this.formContainer.querySelector('form');
             this.formInput = this.form.querySelector('textarea');
@@ -32,13 +47,14 @@ class PageComments {
         if (actionElem === null) return;
         event.preventDefault();
 
-        let action = actionElem.getAttribute('action');
-        if (action === 'edit') this.editComment(actionElem.closest('[comment]'));
+        const action = actionElem.getAttribute('action');
+        const comment = actionElem.closest('[comment]');
+        if (action === 'edit') this.editComment(comment);
         if (action === 'closeUpdateForm') this.closeUpdateForm();
-        if (action === 'delete') this.deleteComment(actionElem.closest('[comment]'));
+        if (action === 'delete') this.deleteComment(comment);
         if (action === 'addComment') this.showForm();
         if (action === 'hideForm') this.hideForm();
-        if (action === 'reply') this.setReply(actionElem.closest('[comment]'));
+        if (action === 'reply') this.setReply(comment);
         if (action === 'remove-reply-to') this.removeReplyTo();
     }
 
@@ -69,14 +85,15 @@ class PageComments {
         };
         this.showLoading(form);
         let commentId = this.editingComment.getAttribute('comment');
-        window.$http.put(`/ajax/comment/${commentId}`, reqData).then(resp => {
+        window.$http.put(`/comment/${commentId}`, reqData).then(resp => {
             let newComment = document.createElement('div');
             newComment.innerHTML = resp.data;
             this.editingComment.innerHTML = newComment.children[0].innerHTML;
-            window.$events.emit('success', window.trans('entities.comment_updated_success'));
+            window.$events.success(this.updatedText);
             window.components.init(this.editingComment);
             this.closeUpdateForm();
             this.editingComment = null;
+        }).catch(window.$events.showValidationErrors).then(() => {
             this.hideLoading(form);
         });
     }
@@ -84,9 +101,9 @@ class PageComments {
     deleteComment(commentElem) {
         let id = commentElem.getAttribute('comment');
         this.showLoading(commentElem.querySelector('[comment-content]'));
-        window.$http.delete(`/ajax/comment/${id}`).then(resp => {
+        window.$http.delete(`/comment/${id}`).then(resp => {
             commentElem.parentNode.removeChild(commentElem);
-            window.$events.emit('success', window.trans('entities.comment_deleted_success'));
+            window.$events.success(this.deletedText);
             this.updateCount();
             this.hideForm();
         });
@@ -101,21 +118,24 @@ class PageComments {
             parent_id: this.parentId || null,
         };
         this.showLoading(this.form);
-        window.$http.post(`/ajax/page/${this.pageId}/comment`, reqData).then(resp => {
+        window.$http.post(`/comment/${this.pageId}`, reqData).then(resp => {
             let newComment = document.createElement('div');
             newComment.innerHTML = resp.data;
             let newElem = newComment.children[0];
             this.container.appendChild(newElem);
             window.components.init(newElem);
-            window.$events.emit('success', window.trans('entities.comment_created_success'));
+            window.$events.success(this.createdText);
             this.resetForm();
             this.updateCount();
+        }).catch(err => {
+            window.$events.showValidationErrors(err);
+            this.hideLoading(this.form);
         });
     }
 
     updateCount() {
         let count = this.container.children.length;
-        this.elem.querySelector('[comments-title]').textContent = window.trans_choice('entities.comment_count', count, {count});
+        this.elem.querySelector('[comments-title]').textContent = window.trans_plural(this.countText, count, {count});
     }
 
     resetForm() {
@@ -129,7 +149,7 @@ class PageComments {
     showForm() {
         this.formContainer.style.display = 'block';
         this.formContainer.parentNode.style.display = 'block';
-        this.elem.querySelector('[comment-add-button-container]').style.display = 'none';
+        this.addButtonContainer.style.display = 'none';
         this.formInput.focus();
         this.formInput.scrollIntoView({behavior: "smooth"});
     }
@@ -137,14 +157,12 @@ class PageComments {
     hideForm() {
         this.formContainer.style.display = 'none';
         this.formContainer.parentNode.style.display = 'none';
-        const addButtonContainer = this.elem.querySelector('[comment-add-button-container]');
         if (this.getCommentCount() > 0) {
-            this.elem.appendChild(addButtonContainer)
+            this.elem.appendChild(this.addButtonContainer)
         } else {
-            const countBar = this.elem.querySelector('[comment-count-bar]');
-            countBar.appendChild(addButtonContainer);
+            this.commentCountBar.appendChild(this.addButtonContainer);
         }
-        addButtonContainer.style.display = 'block';
+        this.addButtonContainer.style.display = 'block';
     }
 
     getCommentCount() {
@@ -154,15 +172,15 @@ class PageComments {
     setReply(commentElem) {
         this.showForm();
         this.parentId = Number(commentElem.getAttribute('local-id'));
-        this.elem.querySelector('[comment-form-reply-to]').style.display = 'block';
-        let replyLink = this.elem.querySelector('[comment-form-reply-to] a');
+        this.replyToRow.style.display = 'block';
+        const replyLink = this.replyToRow.querySelector('a');
         replyLink.textContent = `#${this.parentId}`;
         replyLink.href = `#comment${this.parentId}`;
     }
 
     removeReplyTo() {
         this.parentId = null;
-        this.elem.querySelector('[comment-form-reply-to]').style.display = 'none';
+        this.replyToRow.style.display = 'none';
     }
 
     showLoading(formElem) {
diff --git a/resources/js/components/page-editor.js b/resources/js/components/page-editor.js
new file mode 100644 (file)
index 0000000..266e191
--- /dev/null
@@ -0,0 +1,183 @@
+import * as Dates from "../services/dates";
+import {onSelect} from "../services/dom";
+
+/**
+ * Page Editor
+ * @extends {Component}
+ */
+class PageEditor {
+    setup() {
+        // Options
+        this.draftsEnabled = this.$opts.draftsEnabled === 'true';
+        this.editorType = this.$opts.editorType;
+        this.pageId = Number(this.$opts.pageId);
+        this.isNewDraft = this.$opts.pageNewDraft === 'true';
+        this.hasDefaultTitle = this.$opts.isDefaultTitle || false;
+
+        // Elements
+        this.container = this.$el;
+        this.titleElem = this.$refs.titleContainer.querySelector('input');
+        this.saveDraftButton = this.$refs.saveDraft;
+        this.discardDraftButton = this.$refs.discardDraft;
+        this.discardDraftWrap = this.$refs.discardDraftWrap;
+        this.draftDisplay = this.$refs.draftDisplay;
+        this.draftDisplayIcon = this.$refs.draftDisplayIcon;
+        this.changelogInput = this.$refs.changelogInput;
+        this.changelogDisplay = this.$refs.changelogDisplay;
+
+        // Translations
+        this.draftText = this.$opts.draftText;
+        this.autosaveFailText = this.$opts.autosaveFailText;
+        this.editingPageText = this.$opts.editingPageText;
+        this.draftDiscardedText = this.$opts.draftDiscardedText;
+        this.setChangelogText = this.$opts.setChangelogText;
+
+        // State data
+        this.editorHTML = '';
+        this.editorMarkdown = '';
+        this.autoSave = {
+            interval: null,
+            frequency: 30000,
+            last: 0,
+        };
+
+        if (this.pageId !== 0 && this.draftsEnabled) {
+            window.setTimeout(() => {
+                this.startAutoSave();
+            }, 1000);
+        }
+        this.draftDisplay.innerHTML = this.draftText;
+
+        this.setupListeners();
+        this.setInitialFocus();
+    }
+
+    setupListeners() {
+        // Listen to save events from editor
+        window.$events.listen('editor-save-draft', this.saveDraft.bind(this));
+        window.$events.listen('editor-save-page', this.savePage.bind(this));
+
+        // Listen to content changes from the editor
+        window.$events.listen('editor-html-change', html => {
+            this.editorHTML = html;
+        });
+        window.$events.listen('editor-markdown-change', markdown => {
+            this.editorMarkdown = markdown;
+        });
+
+        // Changelog controls
+        this.changelogInput.addEventListener('change', this.updateChangelogDisplay.bind(this));
+
+        // Draft Controls
+        onSelect(this.saveDraftButton, this.saveDraft.bind(this));
+        onSelect(this.discardDraftButton, this.discardDraft.bind(this));
+    }
+
+    setInitialFocus() {
+        if (this.hasDefaultTitle) {
+            return this.titleElem.select();
+        }
+
+        window.setTimeout(() => {
+            window.$events.emit('editor::focus', '');
+        }, 500);
+    }
+
+    startAutoSave() {
+        let lastContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+        this.autoSaveInterval = window.setInterval(() => {
+            // Stop if manually saved recently to prevent bombarding the server
+            let savedRecently = (Date.now() - this.autoSave.last < (this.autoSave.frequency)/2);
+            if (savedRecently) return;
+            const newContent = this.titleElem.value.trim() + '::' + this.editorHTML;
+            if (newContent !== lastContent) {
+                lastContent = newContent;
+                this.saveDraft();
+            }
+
+        }, this.autoSave.frequency);
+    }
+
+    savePage() {
+        this.container.closest('form').submit();
+    }
+
+    async saveDraft() {
+        const data = {
+            name: this.titleElem.value.trim(),
+            html: this.editorHTML,
+        };
+
+        if (this.editorType === 'markdown') {
+            data.markdown = this.editorMarkdown;
+        }
+
+        try {
+            const resp = await window.$http.put(`/ajax/page/${this.pageId}/save-draft`, data);
+            if (!this.isNewDraft) {
+                this.toggleDiscardDraftVisibility(true);
+            }
+            this.draftNotifyChange(`${resp.data.message} ${Dates.utcTimeStampToLocalTime(resp.data.timestamp)}`);
+            this.autoSave.last = Date.now();
+        } catch (err) {
+            // Save the editor content in LocalStorage as a last resort, just in case.
+            try {
+                const saveKey = `draft-save-fail-${(new Date()).toISOString()}`;
+                window.localStorage.setItem(saveKey, JSON.stringify(data));
+            } catch (err) {}
+
+            window.$events.emit('error', this.autosaveFailText);
+        }
+
+    }
+
+    draftNotifyChange(text) {
+        this.draftDisplay.innerText = text;
+        this.draftDisplayIcon.classList.add('visible');
+        window.setTimeout(() => {
+            this.draftDisplayIcon.classList.remove('visible');
+        }, 2000);
+    }
+
+    async discardDraft() {
+        let response;
+        try {
+            response = await window.$http.get(`/ajax/page/${this.pageId}`);
+        } catch (e) {
+            return console.error(e);
+        }
+
+        if (this.autoSave.interval) {
+            window.clearInterval(this.autoSave.interval);
+        }
+
+        this.draftDisplay.innerText = this.editingPageText;
+        this.toggleDiscardDraftVisibility(false);
+        window.$events.emit('editor-html-update', response.data.html || '');
+        window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
+
+        this.titleElem.value = response.data.name;
+        window.setTimeout(() => {
+            this.startAutoSave();
+        }, 1000);
+        window.$events.emit('success', this.draftDiscardedText);
+
+    }
+
+    updateChangelogDisplay() {
+        let summary = this.changelogInput.value.trim();
+        if (summary.length === 0) {
+            summary = this.setChangelogText;
+        } else if (summary.length > 16) {
+            summary = summary.slice(0, 16) + '...';
+        }
+        this.changelogDisplay.innerText = summary;
+    }
+
+    toggleDiscardDraftVisibility(show) {
+        this.discardDraftWrap.classList.toggle('hidden', !show);
+    }
+
+}
+
+export default PageEditor;
\ No newline at end of file
diff --git a/resources/js/components/popup.js b/resources/js/components/popup.js
new file mode 100644 (file)
index 0000000..13cf69d
--- /dev/null
@@ -0,0 +1,61 @@
+import {fadeIn, fadeOut} from "../services/animations";
+import {onSelect} from "../services/dom";
+
+/**
+ * Popup window that will contain other content.
+ * This component provides the show/hide functionality
+ * with the ability for popup@hide child references to close this.
+ * @extends {Component}
+ */
+class Popup {
+
+    setup() {
+        this.container = this.$el;
+        this.hideButtons = this.$manyRefs.hide || [];
+
+        this.onkeyup = null;
+        this.onHide = null;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        let lastMouseDownTarget = null;
+        this.container.addEventListener('mousedown', event => {
+            lastMouseDownTarget = event.target;
+        });
+
+        this.container.addEventListener('click', event => {
+            if (event.target === this.container && lastMouseDownTarget === this.container) {
+                return this.hide();
+            }
+        });
+
+        onSelect(this.hideButtons, e => this.hide());
+    }
+
+    hide(onComplete = null) {
+        fadeOut(this.container, 240, onComplete);
+        if (this.onkeyup) {
+            window.removeEventListener('keyup', this.onkeyup);
+            this.onkeyup = null;
+        }
+        if (this.onHide) {
+            this.onHide();
+        }
+    }
+
+    show(onComplete = null, onHide = null) {
+        fadeIn(this.container, 240, onComplete);
+
+        this.onkeyup = (event) => {
+            if (event.key === 'Escape') {
+                this.hide();
+            }
+        };
+        window.addEventListener('keyup', this.onkeyup);
+        this.onHide = onHide;
+    }
+
+}
+
+export default Popup;
\ No newline at end of file
diff --git a/resources/js/components/sortable-list.js b/resources/js/components/sortable-list.js
new file mode 100644 (file)
index 0000000..0af0e11
--- /dev/null
@@ -0,0 +1,39 @@
+import Sortable from "sortablejs";
+
+/**
+ * SortableList
+ *
+ * Can have data set on the dragged items by setting a 'data-drag-content' attribute.
+ * This attribute must contain JSON where the keys are content types and the values are
+ * the data to set on the data-transfer.
+ *
+ * @extends {Component}
+ */
+class SortableList {
+    setup() {
+        this.container = this.$el;
+        this.handleSelector = this.$opts.handleSelector;
+
+        const sortable = new Sortable(this.container, {
+            handle: this.handleSelector,
+            animation: 150,
+            onSort: () => {
+                this.$emit('sort', {ids: sortable.toArray()});
+            },
+            setData(dataTransferItem, dragEl) {
+                const jsonContent = dragEl.getAttribute('data-drag-content');
+                if (jsonContent) {
+                    const contentByType = JSON.parse(jsonContent);
+                    for (const [type, content] of Object.entries(contentByType)) {
+                        dataTransferItem.setData(type, content);
+                    }
+                }
+            },
+            revertOnSpill: true,
+            dropBubble: true,
+            dragoverBubble: false,
+        });
+    }
+}
+
+export default SortableList;
\ No newline at end of file
diff --git a/resources/js/components/submit-on-change.js b/resources/js/components/submit-on-change.js
new file mode 100644 (file)
index 0000000..9799672
--- /dev/null
@@ -0,0 +1,19 @@
+/**
+ * Submit on change
+ * Simply submits a parent form when this input is changed.
+ * @extends {Component}
+ */
+class SubmitOnChange {
+
+    setup() {
+        this.$el.addEventListener('change', () => {
+            const form = this.$el.closest('form');
+            if (form) {
+                form.submit();
+            }
+        });
+    }
+
+}
+
+export default SubmitOnChange;
\ No newline at end of file
diff --git a/resources/js/components/tabs.js b/resources/js/components/tabs.js
new file mode 100644 (file)
index 0000000..7121d70
--- /dev/null
@@ -0,0 +1,51 @@
+/**
+ * Tabs
+ * Works by matching 'tabToggle<Key>' with 'tabContent<Key>' sections.
+ * @extends {Component}
+ */
+import {onSelect} from "../services/dom";
+
+class Tabs {
+
+    setup() {
+        this.tabContentsByName = {};
+        this.tabButtonsByName = {};
+        this.allContents = [];
+        this.allButtons = [];
+
+        for (const [key, elems] of Object.entries(this.$manyRefs || {})) {
+            if (key.startsWith('toggle')) {
+                const cleanKey = key.replace('toggle', '').toLowerCase();
+                onSelect(elems, e => this.show(cleanKey));
+                this.allButtons.push(...elems);
+                this.tabButtonsByName[cleanKey] = elems;
+            }
+            if (key.startsWith('content')) {
+                const cleanKey = key.replace('content', '').toLowerCase();
+                this.tabContentsByName[cleanKey] = elems;
+                this.allContents.push(...elems);
+            }
+        }
+    }
+
+    show(key) {
+        this.allContents.forEach(c => {
+            c.classList.add('hidden');
+            c.classList.remove('selected');
+        });
+        this.allButtons.forEach(b => b.classList.remove('selected'));
+
+        const contents = this.tabContentsByName[key] || [];
+        const buttons = this.tabButtonsByName[key] || [];
+        if (contents.length > 0) {
+            contents.forEach(c => {
+                c.classList.remove('hidden')
+                c.classList.add('selected')
+            });
+            buttons.forEach(b => b.classList.add('selected'));
+        }
+    }
+
+}
+
+export default Tabs;
\ No newline at end of file
diff --git a/resources/js/components/tag-manager.js b/resources/js/components/tag-manager.js
new file mode 100644 (file)
index 0000000..99302b6
--- /dev/null
@@ -0,0 +1,32 @@
+/**
+ * TagManager
+ * @extends {Component}
+ */
+class TagManager {
+    setup() {
+        this.addRemoveComponentEl = this.$refs.addRemove;
+        this.container = this.$el;
+        this.rowSelector = this.$opts.rowSelector;
+
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        this.container.addEventListener('change', event => {
+            const addRemoveComponent = this.addRemoveComponentEl.components['add-remove-rows'];
+            if (!this.hasEmptyRows()) {
+                addRemoveComponent.add();
+            }
+        });
+    }
+
+    hasEmptyRows() {
+        const rows = this.container.querySelectorAll(this.rowSelector);
+        const firstEmpty = [...rows].find(row => {
+            return [...row.querySelectorAll('input')].filter(input => input.value).length === 0;
+        });
+        return firstEmpty !== undefined;
+    }
+}
+
+export default TagManager;
\ No newline at end of file
index 1c8c71099434e9c9422f7846be50a4bbc2f769bb..a32e78161649e6337ee45bb6d9ab76f8bbf5fc9c 100644 (file)
@@ -137,7 +137,7 @@ function codePlugin() {
 
         if (!elemIsCodeBlock(selectedNode)) {
             const providedCode = editor.selection.getNode().textContent;
-            window.vues['code-editor'].open(providedCode, '', (code, lang) => {
+            window.components.first('code-editor').open(providedCode, '', (code, lang) => {
                 const wrap = document.createElement('div');
                 wrap.innerHTML = `<pre><code class="language-${lang}"></code></pre>`;
                 wrap.querySelector('code').innerText = code;
@@ -155,7 +155,7 @@ function codePlugin() {
         let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
         let currentCode = selectedNode.querySelector('textarea').textContent;
 
-        window.vues['code-editor'].open(currentCode, lang, (code, lang) => {
+        window.components.first('code-editor').open(currentCode, lang, (code, lang) => {
             const editorElem = selectedNode.querySelector('.CodeMirror');
             const cmInstance = editorElem.CodeMirror;
             if (cmInstance) {
@@ -236,7 +236,7 @@ function codePlugin() {
     });
 }
 
-function drawIoPlugin(drawioUrl, isDarkMode) {
+function drawIoPlugin(drawioUrl, isDarkMode, pageId) {
 
     let pageEditor = null;
     let currentNode = null;
@@ -270,7 +270,6 @@ function drawIoPlugin(drawioUrl, isDarkMode) {
     async function updateContent(pngData) {
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const pageId = Number(document.getElementById('page-editor').getAttribute('page-id'));
 
         // Handle updating an existing image
         if (currentNode) {
@@ -402,6 +401,11 @@ function listenForBookStackEditorEvents(editor) {
         editor.setContent(content);
     });
 
+    // Insert editor content at the current location
+    window.$events.listen('editor::insert', ({html}) => {
+        editor.insertContent(html);
+    });
+
     // Focus on the editor
     window.$events.listen('editor::focus', () => {
         editor.focus();
@@ -410,19 +414,19 @@ function listenForBookStackEditorEvents(editor) {
 
 class WysiwygEditor {
 
-    constructor(elem) {
-        this.elem = elem;
 
-        const pageEditor = document.getElementById('page-editor');
-        this.pageId = pageEditor.getAttribute('page-id');
-        this.textDirection = pageEditor.getAttribute('text-direction');
+    setup() {
+        this.elem = this.$el;
+
+        this.pageId = this.$opts.pageId;
+        this.textDirection = this.$opts.textDirection;
         this.isDarkMode = document.documentElement.classList.contains('dark-mode');
 
-        this.plugins = "image table textcolor paste link autolink fullscreen imagetools code customhr autosave lists codeeditor media";
+        this.plugins = "image table textcolor paste link autolink fullscreen code customhr autosave lists codeeditor media";
         this.loadPlugins();
 
         this.tinyMceConfig = this.getTinyMceConfig();
-        window.$events.emitPublic(elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
+        window.$events.emitPublic(this.elem, 'editor-tinymce::pre-init', {config: this.tinyMceConfig});
         window.tinymce.init(this.tinyMceConfig);
     }
 
@@ -433,7 +437,7 @@ class WysiwygEditor {
         const drawioUrlElem = document.querySelector('[drawio-url]');
         if (drawioUrlElem) {
             const url = drawioUrlElem.getAttribute('drawio-url');
-            drawIoPlugin(url, this.isDarkMode);
+            drawIoPlugin(url, this.isDarkMode, this.pageId);
             this.plugins += ' drawio';
         }
 
@@ -639,6 +643,7 @@ class WysiwygEditor {
 
                 });
 
+                // Custom drop event handling
                 editor.on('drop', function (event) {
                     let dom = editor.dom,
                         rng = tinymce.dom.RangeUtils.getCaretRangeFromPoint(event.clientX, event.clientY, editor.getDoc());
index e0c7b34e5a85d5c22e57236c04346c95b7041a38..ffdb54e191e1557b7c3cbb6e9f6b4402913c40b9 100644 (file)
@@ -7,11 +7,10 @@ window.baseUrl = function(path) {
 };
 
 // Set events and http services on window
-import Events from "./services/events"
+import events from "./services/events"
 import httpInstance from "./services/http"
-const eventManager = new Events();
 window.$http = httpInstance;
-window.$events = eventManager;
+window.$events = events;
 
 // Translation setup
 // Creates a global function with name 'trans' to be used in the same way as Laravel's translation system
@@ -19,14 +18,8 @@ import Translations from "./services/translations"
 const translator = new Translations();
 window.trans = translator.get.bind(translator);
 window.trans_choice = translator.getPlural.bind(translator);
+window.trans_plural = translator.parsePlural.bind(translator);
 
-// Make services available to Vue instances
-import Vue from "vue"
-Vue.prototype.$http = httpInstance;
-Vue.prototype.$events = eventManager;
-
-// Load Vues and components
-import vues from "./vues/vues"
+// Load Components
 import components from "./components"
-vues();
 components();
\ No newline at end of file
index 966a4540e84eeb91117cec8ea89c41609d8b28e6..7a7b2c9bcfce6ce3ae0daf532a9d4be05f8229b7 100644 (file)
@@ -25,17 +25,42 @@ export function onEvents(listenerElement, events, callback) {
 /**
  * Helper to run an action when an element is selected.
  * A "select" is made to be accessible, So can be a click, space-press or enter-press.
- * @param listenerElement
- * @param callback
+ * @param {HTMLElement|Array} elements
+ * @param {function} callback
  */
-export function onSelect(listenerElement, callback) {
-    listenerElement.addEventListener('click', callback);
-    listenerElement.addEventListener('keydown', (event) => {
-        if (event.key === 'Enter' || event.key === ' ') {
-            event.preventDefault();
+export function onSelect(elements, callback) {
+    if (!Array.isArray(elements)) {
+        elements = [elements];
+    }
+
+    for (const listenerElement of elements) {
+        listenerElement.addEventListener('click', callback);
+        listenerElement.addEventListener('keydown', (event) => {
+            if (event.key === 'Enter' || event.key === ' ') {
+                event.preventDefault();
+                callback(event);
+            }
+        });
+    }
+}
+
+/**
+ * Listen to enter press on the given element(s).
+ * @param {HTMLElement|Array} elements
+ * @param {function} callback
+ */
+export function onEnterPress(elements, callback) {
+    if (!Array.isArray(elements)) {
+        elements = [elements];
+    }
+
+    const listener = event => {
+        if (event.key === 'Enter') {
             callback(event);
         }
-    });
+    }
+
+    elements.forEach(e => e.addEventListener('keypress', listener));
 }
 
 /**
@@ -72,4 +97,24 @@ export function findText(selector, text) {
         }
     }
     return null;
+}
+
+/**
+ * Show a loading indicator in the given element.
+ * This will effectively clear the element.
+ * @param {Element} element
+ */
+export function showLoading(element) {
+    element.innerHTML = `<div class="loading-container"><div></div><div></div><div></div></div>`;
+}
+
+/**
+ * Remove any loading indicators within the given element.
+ * @param {Element} element
+ */
+export function removeLoading(element) {
+    const loadingEls = element.querySelectorAll('.loading-container');
+    for (const el of loadingEls) {
+        el.remove();
+    }
 }
\ No newline at end of file
index fa3ed7fdfcb55ebd341ee44543eafd58b697d14d..6668014e7b6913ca4fbee93fe11f69307ada3349 100644 (file)
@@ -1,55 +1,66 @@
+const listeners = {};
+const stack = [];
+
 /**
- * Simple global events manager
+ * Emit a custom event for any handlers to pick-up.
+ * @param {String} eventName
+ * @param {*} eventData
  */
-class Events {
-    constructor() {
-        this.listeners = {};
-        this.stack = [];
+function emit(eventName, eventData) {
+    stack.push({name: eventName, data: eventData});
+    if (typeof listeners[eventName] === 'undefined') return this;
+    let eventsToStart = listeners[eventName];
+    for (let i = 0; i < eventsToStart.length; i++) {
+        let event = eventsToStart[i];
+        event(eventData);
     }
+}
 
-    /**
-     * Emit a custom event for any handlers to pick-up.
-     * @param {String} eventName
-     * @param {*} eventData
-     * @returns {Events}
-     */
-    emit(eventName, eventData) {
-        this.stack.push({name: eventName, data: eventData});
-        if (typeof this.listeners[eventName] === 'undefined') return this;
-        let eventsToStart = this.listeners[eventName];
-        for (let i = 0; i < eventsToStart.length; i++) {
-            let event = eventsToStart[i];
-            event(eventData);
-        }
-        return this;
-    }
+/**
+ * Listen to a custom event and run the given callback when that event occurs.
+ * @param {String} eventName
+ * @param {Function} callback
+ * @returns {Events}
+ */
+function listen(eventName, callback) {
+    if (typeof listeners[eventName] === 'undefined') listeners[eventName] = [];
+    listeners[eventName].push(callback);
+}
 
-    /**
-     * Listen to a custom event and run the given callback when that event occurs.
-     * @param {String} eventName
-     * @param {Function} callback
-     * @returns {Events}
-     */
-    listen(eventName, callback) {
-        if (typeof this.listeners[eventName] === 'undefined') this.listeners[eventName] = [];
-        this.listeners[eventName].push(callback);
-        return this;
-    }
+/**
+ * Emit an event for public use.
+ * Sends the event via the native DOM event handling system.
+ * @param {Element} targetElement
+ * @param {String} eventName
+ * @param {Object} eventData
+ */
+function emitPublic(targetElement, eventName, eventData) {
+    const event = new CustomEvent(eventName, {
+        detail: eventData,
+        bubbles: true
+    });
+    targetElement.dispatchEvent(event);
+}
 
-    /**
-     * Emit an event for public use.
-     * Sends the event via the native DOM event handling system.
-     * @param {Element} targetElement
-     * @param {String} eventName
-     * @param {Object} eventData
-     */
-    emitPublic(targetElement, eventName, eventData) {
-        const event = new CustomEvent(eventName, {
-            detail: eventData,
-            bubbles: true
-        });
-        targetElement.dispatchEvent(event);
+/**
+ * Notify of a http error.
+ * Check for standard scenarios such as validation errors and
+ * formats an error notification accordingly.
+ * @param {Error} error
+ */
+function showValidationErrors(error) {
+    if (!error.status) return;
+    if (error.status === 422 && error.data) {
+        const message = Object.values(error.data).flat().join('\n');
+        emit('error', message);
     }
 }
 
-export default Events;
\ No newline at end of file
+export default {
+    emit,
+    emitPublic,
+    listen,
+    success: (msg) => emit('success', msg),
+    error: (msg) => emit('error', msg),
+    showValidationErrors,
+}
\ No newline at end of file
index 06dac9864a4d4523987e61f4009075ea34ed0594..8ecd6c109168d26a48024c3dbb847b130e3d1592 100644 (file)
@@ -67,11 +67,23 @@ async function dataRequest(method, url, data = null) {
         body: data,
     };
 
+    // Send data as JSON if a plain object
     if (typeof data === 'object' && !(data instanceof FormData)) {
-        options.headers = {'Content-Type': 'application/json'};
+        options.headers = {
+            'Content-Type': 'application/json',
+            'X-Requested-With': 'XMLHttpRequest',
+        };
         options.body = JSON.stringify(data);
     }
 
+    // Ensure FormData instances are sent over POST
+    // Since Laravel does not read multipart/form-data from other types
+    // of request. Hence the addition of the magic _method value.
+    if (data instanceof FormData && method !== 'post') {
+        data.append('_method', method);
+        options.method = 'post';
+    }
+
     return request(url, options)
 }
 
@@ -109,7 +121,7 @@ async function request(url, options = {}) {
 
     const response = await fetch(url, options);
     const content = await getResponseContent(response);
-    return {
+    const returnData = {
         data: content,
         headers: response.headers,
         redirected: response.redirected,
@@ -117,7 +129,13 @@ async function request(url, options = {}) {
         statusText: response.statusText,
         url: response.url,
         original: response,
+    };
+
+    if (!response.ok) {
+        throw returnData;
     }
+
+    return returnData;
 }
 
 /**
index b595a05e6f95b8e9a2d7862adbc5b5d003f6e163..62bb51f56aacb5f0216e0e4621ffdfcae0d34481 100644 (file)
@@ -47,7 +47,19 @@ class Translator {
      */
     getPlural(key, count, replacements) {
         const text = this.getTransText(key);
-        const splitText = text.split('|');
+        return this.parsePlural(text, count, replacements);
+    }
+
+    /**
+     * Parse the given translation and find the correct plural option
+     * to use. Similar format at laravel's 'trans_choice' helper.
+     * @param {String} translation
+     * @param {Number} count
+     * @param {Object} replacements
+     * @returns {String}
+     */
+    parsePlural(translation, count, replacements) {
+        const splitText = translation.split('|');
         const exactCountRegex = /^{([0-9]+)}/;
         const rangeRegex = /^\[([0-9]+),([0-9*]+)]/;
         let result = null;
index b2f2918725004920543a439d6031e3cce57aecc1..de2ca20c13eb934b00a475b817489e9fa29d9670 100644 (file)
@@ -45,4 +45,29 @@ export function scrollAndHighlightElement(element) {
         element.classList.remove('selectFade');
         element.style.backgroundColor = '';
     }, 3000);
+}
+
+/**
+ * Escape any HTML in the given 'unsafe' string.
+ * Take from https://stackoverflow.com/a/6234804.
+ * @param {String} unsafe
+ * @returns {string}
+ */
+export function escapeHtml(unsafe) {
+    return unsafe
+        .replace(/&/g, "&amp;")
+        .replace(/</g, "&lt;")
+        .replace(/>/g, "&gt;")
+        .replace(/"/g, "&quot;")
+        .replace(/'/g, "&#039;");
+}
+
+/**
+ * Generate a random unique ID.
+ *
+ * @returns {string}
+ */
+export function uniqueId() {
+    const S4 = () => (((1+Math.random())*0x10000)|0).toString(16).substring(1);
+    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
 }
\ No newline at end of file
diff --git a/resources/js/vues/attachment-manager.js b/resources/js/vues/attachment-manager.js
deleted file mode 100644 (file)
index 2467c64..0000000
+++ /dev/null
@@ -1,142 +0,0 @@
-import draggable from "vuedraggable";
-import dropzone from "./components/dropzone";
-
-function mounted() {
-    this.pageId = this.$el.getAttribute('page-id');
-    this.file = this.newFile();
-
-    this.$http.get(window.baseUrl(`/attachments/get/page/${this.pageId}`)).then(resp => {
-        this.files = resp.data;
-    }).catch(err => {
-        this.checkValidationErrors('get', err);
-    });
-}
-
-let data = {
-    pageId: null,
-    files: [],
-    fileToEdit: null,
-    file: {},
-    tab: 'list',
-    editTab: 'file',
-    errors: {link: {}, edit: {}, delete: {}}
-};
-
-const components = {dropzone, draggable};
-
-let methods = {
-
-    newFile() {
-        return {page_id: this.pageId};
-    },
-
-    getFileUrl(file) {
-        if (file.external && file.path.indexOf('http') !== 0) {
-            return file.path;
-        }
-        return window.baseUrl(`/attachments/${file.id}`);
-    },
-
-    fileSortUpdate() {
-        this.$http.put(window.baseUrl(`/attachments/sort/page/${this.pageId}`), {files: this.files}).then(resp => {
-            this.$events.emit('success', resp.data.message);
-        }).catch(err => {
-            this.checkValidationErrors('sort', err);
-        });
-    },
-
-    startEdit(file) {
-        this.fileToEdit = Object.assign({}, file);
-        this.fileToEdit.link = file.external ? file.path : '';
-        this.editTab = file.external ? 'link' : 'file';
-    },
-
-    deleteFile(file) {
-        if (!file.deleting) {
-            return this.$set(file, 'deleting', true);
-        }
-
-        this.$http.delete(window.baseUrl(`/attachments/${file.id}`)).then(resp => {
-            this.$events.emit('success', resp.data.message);
-            this.files.splice(this.files.indexOf(file), 1);
-        }).catch(err => {
-            this.checkValidationErrors('delete', err)
-        });
-    },
-
-    uploadSuccess(upload) {
-        this.files.push(upload.data);
-        this.$events.emit('success', trans('entities.attachments_file_uploaded'));
-    },
-
-    uploadSuccessUpdate(upload) {
-        let fileIndex = this.filesIndex(upload.data);
-        if (fileIndex === -1) {
-            this.files.push(upload.data)
-        } else {
-            this.files.splice(fileIndex, 1, upload.data);
-        }
-
-        if (this.fileToEdit && this.fileToEdit.id === upload.data.id) {
-            this.fileToEdit = Object.assign({}, upload.data);
-        }
-        this.$events.emit('success', trans('entities.attachments_file_updated'));
-    },
-
-    checkValidationErrors(groupName, err) {
-        if (typeof err.response.data === "undefined" && typeof err.response.data === "undefined") return;
-        this.errors[groupName] = err.response.data;
-    },
-
-    getUploadUrl(file) {
-        let url = window.baseUrl(`/attachments/upload`);
-        if (typeof file !== 'undefined') url += `/${file.id}`;
-        return url;
-    },
-
-    cancelEdit() {
-        this.fileToEdit = null;
-    },
-
-    attachNewLink(file) {
-        file.uploaded_to = this.pageId;
-        this.errors.link = {};
-        this.$http.post(window.baseUrl('/attachments/link'), file).then(resp => {
-            this.files.push(resp.data);
-            this.file = this.newFile();
-            this.$events.emit('success', trans('entities.attachments_link_attached'));
-        }).catch(err => {
-            this.checkValidationErrors('link', err);
-        });
-    },
-
-    updateFile(file) {
-        $http.put(window.baseUrl(`/attachments/${file.id}`), file).then(resp => {
-            let search = this.filesIndex(resp.data);
-            if (search === -1) {
-                this.files.push(resp.data);
-            } else {
-                this.files.splice(search, 1, resp.data);
-            }
-
-            if (this.fileToEdit && !file.external) this.fileToEdit.link = '';
-            this.fileToEdit = false;
-
-            this.$events.emit('success', trans('entities.attachments_updated_success'));
-        }).catch(err => {
-            this.checkValidationErrors('edit', err);
-        });
-    },
-
-    filesIndex(file) {
-        for (let i = 0, len = this.files.length; i < len; i++) {
-            if (this.files[i].id === file.id) return i;
-        }
-        return -1;
-    }
-
-};
-
-export default {
-    data, methods, mounted, components,
-};
\ No newline at end of file
diff --git a/resources/js/vues/code-editor.js b/resources/js/vues/code-editor.js
deleted file mode 100644 (file)
index f888e62..0000000
+++ /dev/null
@@ -1,46 +0,0 @@
-import codeLib from "../services/code";
-
-const methods = {
-    show() {
-        if (!this.editor) this.editor = codeLib.popupEditor(this.$refs.editor, this.language);
-        this.$refs.overlay.components.overlay.show(() => {
-            codeLib.updateLayout(this.editor);
-            this.editor.focus();
-        });
-    },
-    hide() {
-        this.$refs.overlay.components.overlay.hide();
-    },
-    updateEditorMode(language) {
-        codeLib.setMode(this.editor, language, this.editor.getValue());
-    },
-    updateLanguage(lang) {
-        this.language = lang;
-        this.updateEditorMode(lang);
-    },
-    open(code, language, callback) {
-        this.show();
-        this.updateEditorMode(language);
-        this.language = language;
-        codeLib.setContent(this.editor, code);
-        this.code = code;
-        this.callback = callback;
-    },
-    save() {
-        if (!this.callback) return;
-        this.callback(this.editor.getValue(), this.language);
-        this.hide();
-    }
-};
-
-const data = {
-    editor: null,
-    language: '',
-    code: '',
-    callback: null
-};
-
-export default {
-    methods,
-    data
-};
\ No newline at end of file
diff --git a/resources/js/vues/components/autosuggest.js b/resources/js/vues/components/autosuggest.js
deleted file mode 100644 (file)
index 9832a9e..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-
-const template = `
-    <div>
-        <input :value="value" :autosuggest-type="type" ref="input"
-            :placeholder="placeholder" :name="name"
-            type="text"
-            @input="inputUpdate($event.target.value)" @focus="inputUpdate($event.target.value)"
-            @blur="inputBlur"
-            @keydown="inputKeydown"
-            :aria-label="placeholder"
-        />
-        <ul class="suggestion-box" v-if="showSuggestions">
-            <li v-for="(suggestion, i) in suggestions"
-                @click="selectSuggestion(suggestion)"
-                :class="{active: (i === active)}">{{suggestion}}</li>
-        </ul>
-    </div>
-`;
-
-function data() {
-    return {
-        suggestions: [],
-        showSuggestions: false,
-        active: 0,
-    };
-}
-
-const ajaxCache = {};
-
-const props = ['url', 'type', 'value', 'placeholder', 'name'];
-
-function getNameInputVal(valInput) {
-    let parentRow = valInput.parentNode.parentNode;
-    let nameInput = parentRow.querySelector('[autosuggest-type="name"]');
-    return (nameInput === null) ? '' : nameInput.value;
-}
-
-const methods = {
-
-    inputUpdate(inputValue) {
-        this.$emit('input', inputValue);
-        let params = {};
-
-        if (this.type === 'value') {
-            let nameVal = getNameInputVal(this.$el);
-            if (nameVal !== "") params.name = nameVal;
-        }
-
-        this.getSuggestions(inputValue.slice(0, 3), params).then(suggestions => {
-            if (inputValue.length === 0) {
-                this.displaySuggestions(suggestions.slice(0, 6));
-                return;
-            }
-            // Filter to suggestions containing searched term
-            suggestions = suggestions.filter(item => {
-                return item.toLowerCase().indexOf(inputValue.toLowerCase()) !== -1;
-            }).slice(0, 4);
-            this.displaySuggestions(suggestions);
-        });
-    },
-
-    inputBlur() {
-        setTimeout(() => {
-            this.$emit('blur');
-            this.showSuggestions = false;
-        }, 100);
-    },
-
-    inputKeydown(event) {
-        if (event.key === 'Enter') event.preventDefault();
-        if (!this.showSuggestions) return;
-
-        // Down arrow
-        if (event.key === 'ArrowDown') {
-            this.active = (this.active === this.suggestions.length - 1) ? 0 : this.active+1;
-        }
-        // Up Arrow
-        else if (event.key === 'ArrowUp') {
-            this.active = (this.active === 0) ? this.suggestions.length - 1 : this.active-1;
-        }
-        // Enter key
-        else if ((event.key === 'Enter') && !event.shiftKey) {
-            this.selectSuggestion(this.suggestions[this.active]);
-        }
-        // Escape key
-        else if (event.key === 'Escape') {
-            this.showSuggestions = false;
-        }
-    },
-
-    displaySuggestions(suggestions) {
-        if (suggestions.length === 0) {
-            this.suggestions = [];
-            this.showSuggestions = false;
-            return;
-        }
-
-        this.suggestions = suggestions;
-        this.showSuggestions = true;
-        this.active = 0;
-    },
-
-    selectSuggestion(suggestion) {
-        this.$refs.input.value = suggestion;
-        this.$refs.input.focus();
-        this.$emit('input', suggestion);
-        this.showSuggestions = false;
-    },
-
-    /**
-     * Get suggestions from BookStack. Store and use local cache if already searched.
-     * @param {String} input
-     * @param {Object} params
-     */
-    getSuggestions(input, params) {
-        params.search = input;
-        const cacheKey = `${this.url}:${JSON.stringify(params)}`;
-
-        if (typeof ajaxCache[cacheKey] !== "undefined") {
-            return Promise.resolve(ajaxCache[cacheKey]);
-        }
-
-        return this.$http.get(this.url, params).then(resp => {
-            ajaxCache[cacheKey] = resp.data;
-            return resp.data;
-        });
-    }
-
-};
-
-export default {template, data, props, methods};
\ No newline at end of file
diff --git a/resources/js/vues/components/dropzone.js b/resources/js/vues/components/dropzone.js
deleted file mode 100644 (file)
index 1c04572..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-import DropZone from "dropzone";
-import { fadeOut } from "../../services/animations";
-
-const template = `
-    <div class="dropzone-container text-center">
-        <button type="button" class="dz-message">{{placeholder}}</button>
-    </div>
-`;
-
-const props = ['placeholder', 'uploadUrl', 'uploadedTo'];
-
-function mounted() {
-   const container = this.$el;
-   const _this = this;
-   this._dz = new DropZone(container, {
-        addRemoveLinks: true,
-        dictRemoveFile: trans('components.image_upload_remove'),
-        timeout: Number(window.uploadTimeout) || 60000,
-        maxFilesize: Number(window.uploadLimit) || 256,
-        url: function() {
-            return _this.uploadUrl;
-        },
-        init: function () {
-            const dz = this;
-
-            dz.on('sending', function (file, xhr, data) {
-                const token = window.document.querySelector('meta[name=token]').getAttribute('content');
-                data.append('_token', token);
-                const uploadedTo = typeof _this.uploadedTo === 'undefined' ? 0 : _this.uploadedTo;
-                data.append('uploaded_to', uploadedTo);
-
-                xhr.ontimeout = function (e) {
-                    dz.emit('complete', file);
-                    dz.emit('error', file, trans('errors.file_upload_timeout'));
-                }
-            });
-
-            dz.on('success', function (file, data) {
-                _this.$emit('success', {file, data});
-                fadeOut(file.previewElement, 800, () => {
-                    dz.removeFile(file);
-                });
-            });
-
-            dz.on('error', function (file, errorMessage, xhr) {
-                _this.$emit('error', {file, errorMessage, xhr});
-
-                function setMessage(message) {
-                    const messsageEl = file.previewElement.querySelector('[data-dz-errormessage]');
-                    messsageEl.textContent = message;
-                }
-
-                if (xhr && xhr.status === 413) {
-                    setMessage(trans('errors.server_upload_limit'))
-                } else if (errorMessage.file) {
-                    setMessage(errorMessage.file);
-                }
-
-            });
-        }
-   });
-}
-
-function data() {
-    return {};
-}
-
-const methods = {
-    onClose: function () {
-        this._dz.removeAllFiles(true);
-    }
-};
-
-export default {
-    template,
-    props,
-    mounted,
-    data,
-    methods
-};
diff --git a/resources/js/vues/entity-dashboard.js b/resources/js/vues/entity-dashboard.js
deleted file mode 100644 (file)
index d10da70..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-let data = {
-    id: null,
-    type: '',
-    searching: false,
-    searchTerm: '',
-    searchResults: '',
-};
-
-let computed = {
-
-};
-
-let methods = {
-
-    searchBook() {
-        if (this.searchTerm.trim().length === 0) return;
-        this.searching = true;
-        this.searchResults = '';
-        let url = window.baseUrl(`/search/${this.type}/${this.id}`);
-        url += `?term=${encodeURIComponent(this.searchTerm)}`;
-        this.$http.get(url).then(resp => {
-            this.searchResults = resp.data;
-        });
-    },
-
-    checkSearchForm() {
-        this.searching = this.searchTerm > 0;
-    },
-
-    clearSearch() {
-        this.searching = false;
-        this.searchTerm = '';
-    }
-
-};
-
-function mounted() {
-    this.id = Number(this.$el.getAttribute('entity-id'));
-    this.type = this.$el.getAttribute('entity-type');
-}
-
-export default {
-    data, computed, methods, mounted
-};
\ No newline at end of file
diff --git a/resources/js/vues/image-manager.js b/resources/js/vues/image-manager.js
deleted file mode 100644 (file)
index 6df12d1..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-import * as Dates from "../services/dates";
-import dropzone from "./components/dropzone";
-
-let page = 1;
-let previousClickTime = 0;
-let previousClickImage = 0;
-let dataLoaded = false;
-let callback = false;
-let baseUrl = '';
-
-let preSearchImages = [];
-let preSearchHasMore = false;
-
-const data = {
-    images: [],
-
-    imageType: false,
-    uploadedTo: false,
-
-    selectedImage: false,
-    dependantPages: false,
-    showing: false,
-    filter: null,
-    hasMore: false,
-    searching: false,
-    searchTerm: '',
-
-    imageUpdateSuccess: false,
-    imageDeleteSuccess: false,
-    deleteConfirm: false,
-};
-
-const methods = {
-
-    show(providedCallback, imageType = null) {
-        callback = providedCallback;
-        this.showing = true;
-        this.$el.children[0].components.overlay.show();
-
-        // Get initial images if they have not yet been loaded in.
-        if (dataLoaded && imageType === this.imageType) return;
-        if (imageType) {
-            this.imageType = imageType;
-            this.resetState();
-        }
-        this.fetchData();
-        dataLoaded = true;
-    },
-
-    hide() {
-        if (this.$refs.dropzone) {
-            this.$refs.dropzone.onClose();
-        }
-        this.showing = false;
-        this.selectedImage = false;
-        this.$el.children[0].components.overlay.hide();
-    },
-
-    async fetchData() {
-        const params = {
-            page,
-            search: this.searching ? this.searchTerm : null,
-            uploaded_to: this.uploadedTo || null,
-            filter_type: this.filter,
-        };
-
-        const {data} = await this.$http.get(baseUrl, params);
-        this.images = this.images.concat(data.images);
-        this.hasMore = data.has_more;
-        page++;
-    },
-
-    setFilterType(filterType) {
-        this.filter = filterType;
-        this.resetState();
-        this.fetchData();
-    },
-
-    resetState() {
-        this.cancelSearch();
-        this.resetListView();
-        this.deleteConfirm = false;
-        baseUrl = window.baseUrl(`/images/${this.imageType}`);
-    },
-
-    resetListView() {
-        this.images = [];
-        this.hasMore = false;
-        page = 1;
-    },
-
-    searchImages() {
-        if (this.searchTerm === '') return this.cancelSearch();
-
-        // Cache current settings for later
-        if (!this.searching) {
-            preSearchImages = this.images;
-            preSearchHasMore = this.hasMore;
-        }
-
-        this.searching = true;
-        this.resetListView();
-        this.fetchData();
-    },
-
-    cancelSearch() {
-        if (!this.searching) return;
-        this.searching = false;
-        this.searchTerm = '';
-        this.images = preSearchImages;
-        this.hasMore = preSearchHasMore;
-    },
-
-    imageSelect(image) {
-        const dblClickTime = 300;
-        const currentTime = Date.now();
-        const timeDiff = currentTime - previousClickTime;
-        const isDblClick = timeDiff < dblClickTime && image.id === previousClickImage;
-
-        if (isDblClick) {
-            this.callbackAndHide(image);
-        } else {
-            this.selectedImage = image;
-            this.deleteConfirm = false;
-            this.dependantPages = false;
-        }
-
-        previousClickTime = currentTime;
-        previousClickImage = image.id;
-    },
-
-    callbackAndHide(imageResult) {
-        if (callback) callback(imageResult);
-        this.hide();
-    },
-
-    async saveImageDetails() {
-        let url = window.baseUrl(`/images/${this.selectedImage.id}`);
-        try {
-            await this.$http.put(url, this.selectedImage)
-        } catch (error) {
-            if (error.response.status === 422) {
-                let errors = error.response.data;
-                let message = '';
-                Object.keys(errors).forEach((key) => {
-                    message += errors[key].join('\n');
-                });
-                this.$events.emit('error', message);
-            }
-        }
-    },
-
-    async deleteImage() {
-
-        if (!this.deleteConfirm) {
-            const url = window.baseUrl(`/images/usage/${this.selectedImage.id}`);
-            try {
-                const {data} = await this.$http.get(url);
-                this.dependantPages = data;
-            } catch (error) {
-                console.error(error);
-            }
-            this.deleteConfirm = true;
-            return;
-        }
-
-        const url = window.baseUrl(`/images/${this.selectedImage.id}`);
-        await this.$http.delete(url);
-        this.images.splice(this.images.indexOf(this.selectedImage), 1);
-        this.selectedImage = false;
-        this.$events.emit('success', trans('components.image_delete_success'));
-        this.deleteConfirm = false;
-    },
-
-    getDate(stringDate) {
-        return Dates.formatDateTime(new Date(stringDate));
-    },
-
-    uploadSuccess(event) {
-        this.images.unshift(event.data);
-        this.$events.emit('success', trans('components.image_upload_success'));
-    },
-};
-
-const computed = {
-    uploadUrl() {
-        return window.baseUrl(`/images/${this.imageType}`);
-    }
-};
-
-function mounted() {
-    window.ImageManager = this;
-    this.imageType = this.$el.getAttribute('image-type');
-    this.uploadedTo = this.$el.getAttribute('uploaded-to');
-    baseUrl = window.baseUrl('/images/' + this.imageType)
-}
-
-export default {
-    mounted,
-    methods,
-    data,
-    computed,
-    components: {dropzone},
-};
diff --git a/resources/js/vues/page-editor.js b/resources/js/vues/page-editor.js
deleted file mode 100644 (file)
index a79ad20..0000000
+++ /dev/null
@@ -1,167 +0,0 @@
-import * as Dates from "../services/dates";
-
-let autoSaveFrequency = 30;
-
-let autoSave = false;
-let draftErroring = false;
-
-let currentContent = {
-    title: false,
-    html: false
-};
-
-let lastSave = 0;
-
-function mounted() {
-    let elem = this.$el;
-    this.draftsEnabled = elem.getAttribute('drafts-enabled') === 'true';
-    this.editorType = elem.getAttribute('editor-type');
-    this.pageId= Number(elem.getAttribute('page-id'));
-    this.isNewDraft = Number(elem.getAttribute('page-new-draft')) === 1;
-    this.isUpdateDraft = Number(elem.getAttribute('page-update-draft')) === 1;
-    this.titleElem = elem.querySelector('input[name=name]');
-    this.hasDefaultTitle = this.titleElem.closest('[is-default-value]') !== null;
-
-    if (this.pageId !== 0 && this.draftsEnabled) {
-        window.setTimeout(() => {
-            this.startAutoSave();
-        }, 1000);
-    }
-
-    if (this.isUpdateDraft || this.isNewDraft) {
-        this.draftText = trans('entities.pages_editing_draft');
-    } else {
-        this.draftText = trans('entities.pages_editing_page');
-    }
-
-    // Listen to save events from editor
-    window.$events.listen('editor-save-draft', this.saveDraft);
-    window.$events.listen('editor-save-page', this.savePage);
-
-    // Listen to content changes from the editor
-    window.$events.listen('editor-html-change', html => {
-        this.editorHTML = html;
-    });
-    window.$events.listen('editor-markdown-change', markdown => {
-        this.editorMarkdown = markdown;
-    });
-
-    this.setInitialFocus();
-}
-
-let data = {
-    draftsEnabled: false,
-    editorType: 'wysiwyg',
-    pagedId: 0,
-    isNewDraft: false,
-    isUpdateDraft: false,
-
-    draftText: '',
-    draftUpdated : false,
-    changeSummary: '',
-
-    editorHTML: '',
-    editorMarkdown: '',
-
-    hasDefaultTitle: false,
-    titleElem: null,
-};
-
-let methods = {
-
-    setInitialFocus() {
-        if (this.hasDefaultTitle) {
-            this.titleElem.select();
-        } else {
-            window.setTimeout(() => {
-                this.$events.emit('editor::focus', '');
-            }, 500);
-        }
-    },
-
-    startAutoSave() {
-        currentContent.title = this.titleElem.value.trim();
-        currentContent.html = this.editorHTML;
-
-        autoSave = window.setInterval(() => {
-            // Return if manually saved recently to prevent bombarding the server
-            if (Date.now() - lastSave < (1000 * autoSaveFrequency)/2) return;
-            const newTitle = this.titleElem.value.trim();
-            const newHtml = this.editorHTML;
-
-            if (newTitle !== currentContent.title || newHtml !== currentContent.html) {
-                currentContent.html = newHtml;
-                currentContent.title = newTitle;
-                this.saveDraft();
-            }
-
-        }, 1000 * autoSaveFrequency);
-    },
-
-    saveDraft() {
-        if (!this.draftsEnabled) return;
-
-        const data = {
-            name: this.titleElem.value.trim(),
-            html: this.editorHTML
-        };
-
-        if (this.editorType === 'markdown') data.markdown = this.editorMarkdown;
-
-        const url = window.baseUrl(`/ajax/page/${this.pageId}/save-draft`);
-        window.$http.put(url, data).then(response => {
-            draftErroring = false;
-            if (!this.isNewDraft) this.isUpdateDraft = true;
-            this.draftNotifyChange(`${response.data.message} ${Dates.utcTimeStampToLocalTime(response.data.timestamp)}`);
-            lastSave = Date.now();
-        }, errorRes => {
-            if (draftErroring) return;
-            window.$events.emit('error', trans('errors.page_draft_autosave_fail'));
-            draftErroring = true;
-        });
-    },
-
-    savePage() {
-        this.$el.closest('form').submit();
-    },
-
-    draftNotifyChange(text) {
-        this.draftText = text;
-        this.draftUpdated = true;
-        window.setTimeout(() => {
-            this.draftUpdated = false;
-        }, 2000);
-    },
-
-    discardDraft() {
-        let url = window.baseUrl(`/ajax/page/${this.pageId}`);
-        window.$http.get(url).then(response => {
-            if (autoSave) window.clearInterval(autoSave);
-
-            this.draftText = trans('entities.pages_editing_page');
-            this.isUpdateDraft = false;
-            window.$events.emit('editor-html-update', response.data.html);
-            window.$events.emit('editor-markdown-update', response.data.markdown || response.data.html);
-
-            this.titleElem.value = response.data.name;
-            window.setTimeout(() => {
-                this.startAutoSave();
-            }, 1000);
-            window.$events.emit('success', trans('entities.pages_draft_discarded'));
-        });
-    },
-
-};
-
-let computed = {
-    changeSummaryShort() {
-        let len = this.changeSummary.length;
-        if (len === 0) return trans('entities.pages_edit_set_changelog');
-        if (len <= 16) return this.changeSummary;
-        return this.changeSummary.slice(0, 16) + '...';
-    }
-};
-
-export default {
-    mounted, data, methods, computed,
-};
\ No newline at end of file
diff --git a/resources/js/vues/search.js b/resources/js/vues/search.js
deleted file mode 100644 (file)
index c0b828b..0000000
+++ /dev/null
@@ -1,193 +0,0 @@
-import * as Dates from "../services/dates";
-
-let data = {
-    terms: '',
-    termString : '',
-    search: {
-        type: {
-            page: true,
-            chapter: true,
-            book: true,
-            bookshelf: true,
-        },
-        exactTerms: [],
-        tagTerms: [],
-        option: {},
-        dates: {
-            updated_after: false,
-            updated_before: false,
-            created_after: false,
-            created_before: false,
-        }
-    }
-};
-
-let computed = {
-
-};
-
-let methods = {
-
-    appendTerm(term) {
-        this.termString += ' ' + term;
-        this.termString = this.termString.replace(/\s{2,}/g, ' ');
-        this.termString = this.termString.replace(/^\s+/, '');
-        this.termString = this.termString.replace(/\s+$/, '');
-    },
-
-    exactParse(searchString) {
-        this.search.exactTerms = [];
-        let exactFilter = /"(.+?)"/g;
-        let matches;
-        while ((matches = exactFilter.exec(searchString)) !== null) {
-            this.search.exactTerms.push(matches[1]);
-        }
-    },
-
-    exactChange() {
-        let exactFilter = /"(.+?)"/g;
-        this.termString = this.termString.replace(exactFilter, '');
-        let matchesTerm = this.search.exactTerms.filter(term =>  term.trim() !== '').map(term => `"${term}"`).join(' ');
-        this.appendTerm(matchesTerm);
-    },
-
-    addExact() {
-        this.search.exactTerms.push('');
-        setTimeout(() => {
-            let exactInputs = document.querySelectorAll('.exact-input');
-            exactInputs[exactInputs.length - 1].focus();
-        }, 100);
-    },
-
-    removeExact(index) {
-        this.search.exactTerms.splice(index, 1);
-        this.exactChange();
-    },
-
-    tagParse(searchString) {
-        this.search.tagTerms = [];
-        let tagFilter = /\[(.+?)\]/g;
-        let matches;
-        while ((matches = tagFilter.exec(searchString)) !== null) {
-            this.search.tagTerms.push(matches[1]);
-        }
-    },
-
-    tagChange() {
-        let tagFilter = /\[(.+?)\]/g;
-        this.termString = this.termString.replace(tagFilter, '');
-        let matchesTerm = this.search.tagTerms.filter(term => {
-            return term.trim() !== '';
-        }).map(term => {
-            return `[${term}]`
-        }).join(' ');
-        this.appendTerm(matchesTerm);
-    },
-
-    addTag() {
-        this.search.tagTerms.push('');
-        setTimeout(() => {
-            let tagInputs = document.querySelectorAll('.tag-input');
-            tagInputs[tagInputs.length - 1].focus();
-        }, 100);
-    },
-
-    removeTag(index) {
-        this.search.tagTerms.splice(index, 1);
-        this.tagChange();
-    },
-
-    typeParse(searchString) {
-        let typeFilter = /{\s?type:\s?(.*?)\s?}/;
-        let match = searchString.match(typeFilter);
-        let type = this.search.type;
-        if (!match) {
-            type.page = type.book = type.chapter = type.bookshelf = true;
-            return;
-        }
-        let splitTypes = match[1].replace(/ /g, '').split('|');
-        type.page = (splitTypes.indexOf('page') !== -1);
-        type.chapter = (splitTypes.indexOf('chapter') !== -1);
-        type.book = (splitTypes.indexOf('book') !== -1);
-        type.bookshelf = (splitTypes.indexOf('bookshelf') !== -1);
-    },
-
-    typeChange() {
-        let typeFilter = /{\s?type:\s?(.*?)\s?}/;
-        let type = this.search.type;
-        if (type.page === type.chapter === type.book === type.bookshelf) {
-            this.termString = this.termString.replace(typeFilter, '');
-            return;
-        }
-        let selectedTypes = Object.keys(type).filter(type => this.search.type[type]).join('|');
-        let typeTerm = '{type:'+selectedTypes+'}';
-        if (this.termString.match(typeFilter)) {
-            this.termString = this.termString.replace(typeFilter, typeTerm);
-            return;
-        }
-        this.appendTerm(typeTerm);
-    },
-
-    optionParse(searchString) {
-        let optionFilter = /{([a-z_\-:]+?)}/gi;
-        let matches;
-        while ((matches = optionFilter.exec(searchString)) !== null) {
-            this.search.option[matches[1].toLowerCase()] = true;
-        }
-    },
-
-    optionChange(optionName) {
-        let isChecked = this.search.option[optionName];
-        if (isChecked) {
-            this.appendTerm(`{${optionName}}`);
-        } else {
-            this.termString = this.termString.replace(`{${optionName}}`, '');
-        }
-    },
-
-    updateSearch(e) {
-        e.preventDefault();
-        window.location = window.baseUrl('/search?term=' + encodeURIComponent(this.termString));
-    },
-
-    enableDate(optionName) {
-        this.search.dates[optionName.toLowerCase()] = Dates.getCurrentDay();
-        this.dateChange(optionName);
-    },
-
-    dateParse(searchString) {
-        let dateFilter = /{([a-z_\-]+?):([a-z_\-0-9]+?)}/gi;
-        let dateTags = Object.keys(this.search.dates);
-        let matches;
-        while ((matches = dateFilter.exec(searchString)) !== null) {
-            if (dateTags.indexOf(matches[1]) === -1) continue;
-            this.search.dates[matches[1].toLowerCase()] = matches[2];
-        }
-    },
-
-    dateChange(optionName) {
-        let dateFilter = new RegExp('{\\s?'+optionName+'\\s?:([a-z_\\-0-9]+?)}', 'gi');
-        this.termString = this.termString.replace(dateFilter, '');
-        if (!this.search.dates[optionName]) return;
-        this.appendTerm(`{${optionName}:${this.search.dates[optionName]}}`);
-    },
-
-    dateRemove(optionName) {
-        this.search.dates[optionName] = false;
-        this.dateChange(optionName);
-    }
-
-};
-
-function created() {
-    this.termString = document.querySelector('[name=searchTerm]').value;
-    this.typeParse(this.termString);
-    this.exactParse(this.termString);
-    this.tagParse(this.termString);
-    this.optionParse(this.termString);
-    this.dateParse(this.termString);
-}
-
-export default {
-    data, computed, methods, created
-};
diff --git a/resources/js/vues/tag-manager.js b/resources/js/vues/tag-manager.js
deleted file mode 100644 (file)
index 65233cb..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-import draggable from 'vuedraggable';
-import autosuggest from './components/autosuggest';
-
-const data = {
-    entityId: false,
-    entityType: null,
-    tags: [],
-};
-
-const components = {draggable, autosuggest};
-const directives = {};
-
-const methods = {
-
-    addEmptyTag() {
-        this.tags.push({name: '', value: '', key: Math.random().toString(36).substring(7)});
-    },
-
-    /**
-     * When an tag changes check if another empty editable field needs to be added onto the end.
-     * @param tag
-     */
-    tagChange(tag) {
-        let tagPos = this.tags.indexOf(tag);
-        if (tagPos === this.tags.length-1 && (tag.name !== '' || tag.value !== '')) this.addEmptyTag();
-    },
-
-    /**
-     * When an tag field loses focus check the tag to see if its
-     * empty and therefore could be removed from the list.
-     * @param tag
-     */
-    tagBlur(tag) {
-        let isLast = (this.tags.indexOf(tag) === this.tags.length-1);
-        if (tag.name !== '' || tag.value !== '' || isLast) return;
-        let cPos = this.tags.indexOf(tag);
-        this.tags.splice(cPos, 1);
-    },
-
-    removeTag(tag) {
-        let tagPos = this.tags.indexOf(tag);
-        if (tagPos === -1) return;
-        this.tags.splice(tagPos, 1);
-    },
-
-    getTagFieldName(index, key) {
-        return `tags[${index}][${key}]`;
-    },
-};
-
-function mounted() {
-    this.entityId = Number(this.$el.getAttribute('entity-id'));
-    this.entityType = this.$el.getAttribute('entity-type');
-
-    let url = window.baseUrl(`/ajax/tags/get/${this.entityType}/${this.entityId}`);
-    this.$http.get(url).then(response => {
-        let tags = response.data;
-        for (let i = 0, len = tags.length; i < len; i++) {
-            tags[i].key = Math.random().toString(36).substring(7);
-        }
-        this.tags = tags;
-        this.addEmptyTag();
-    });
-}
-
-export default {
-    data, methods, mounted, components, directives
-};
\ No newline at end of file
diff --git a/resources/js/vues/vues.js b/resources/js/vues/vues.js
deleted file mode 100644 (file)
index ec19237..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-import Vue from "vue";
-
-function exists(id) {
-    return document.getElementById(id) !== null;
-}
-
-import searchSystem from "./search";
-import entityDashboard from "./entity-dashboard";
-import codeEditor from "./code-editor";
-import imageManager from "./image-manager";
-import tagManager from "./tag-manager";
-import attachmentManager from "./attachment-manager";
-import pageEditor from "./page-editor";
-
-let vueMapping = {
-    'search-system': searchSystem,
-    'entity-dashboard': entityDashboard,
-    'code-editor': codeEditor,
-    'image-manager': imageManager,
-    'tag-manager': tagManager,
-    'attachment-manager': attachmentManager,
-    'page-editor': pageEditor,
-};
-
-window.vues = {};
-
-function load() {
-    let ids = Object.keys(vueMapping);
-    for (let i = 0, len = ids.length; i < len; i++) {
-        if (!exists(ids[i])) continue;
-        let config = vueMapping[ids[i]];
-        config.el = '#' + ids[i];
-        window.vues[ids[i]] = new Vue(config);
-    }
-}
-
-export default load;
-
-
-
index 348eba3985de052b0d1dd47d16436ecd8a18f3df..d7c8ed798faae1d2f6911254b45338f2c2219667 100644 (file)
@@ -36,7 +36,7 @@ return [
     'book_sort_notification'      => 'تمت إعادة سرد الكتاب بنجاح',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
+    'bookshelf_create'            => 'تم إنشاء رف الكتب',
     'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
     'bookshelf_update'                 => 'updated bookshelf',
     'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
index 193844390f9b933e88beabca0ba92dab552378ff..220b54892a92a417533e899f18bbe3443377db01 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'نسخ',
     'reply' => 'رد',
     'delete' => 'حذف',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'بحث',
     'search_clear' => 'مسح البحث',
     'reset' => 'إعادة تعيين',
index aa3935bd900f5e7f4dba7054fd863a3fae08dbfb..74d9bcdc7853d4c59b75901013c406e02a3ba39e 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'المزيد',
     'image_image_name' => 'اسم الصورة',
     'image_delete_used' => 'هذه الصورة مستخدمة بالصفحات أدناه.',
-    'image_delete_confirm' => 'اضغط زر الحذف مرة أخرى لتأكيد حذف هذه الصورة.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'تحديد الصورة',
     'image_dropzone' => 'قم بإسقاط الصورة أو اضغط هنا للرفع',
     'images_deleted' => 'تم حذف الصور',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'تعديل الشفرة',
     'code_language' => 'لغة الشفرة',
     'code_content' => 'محتويات الشفرة',
+    'code_session_history' => 'Session History',
     'code_save' => 'حفظ الشفرة',
 ];
index 9278c8cf37caee241dfa456457f1b1948046ce92..31835cb32d13598edaa98b190ceb63d84a860389 100644 (file)
@@ -11,7 +11,7 @@ return [
     'recently_updated_pages' => 'صفحات حُدثت مؤخراً',
     'recently_created_chapters' => 'فصول أنشئت مؤخراً',
     'recently_created_books' => 'كتب أنشئت مؤخراً',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'الأرفف المنشأة مؤخراً',
     'recently_update' => 'حُدثت مؤخراً',
     'recently_viewed' => 'عُرضت مؤخراً',
     'recent_activity' => 'نشاطات حديثة',
@@ -47,17 +47,18 @@ return [
     'search_no_pages' => 'لم يطابق بحثكم أي صفحة',
     'search_for_term' => 'ابحث عن :term',
     'search_more' => 'المزيد من النتائج',
-    'search_filters' => 'تصفية البحث',
+    'search_advanced' => 'بحث مفصل',
+    'search_terms' => 'البحث باستخدام المصطلحات',
     'search_content_type' => 'نوع المحتوى',
     'search_exact_matches' => 'نتائج مطابقة تماماً',
     'search_tags' => 'بحث الوسوم',
-    'search_options' => 'Options',
+    'search_options' => 'الخيارات',
     'search_viewed_by_me' => 'تم استعراضها من قبلي',
     'search_not_viewed_by_me' => 'لم يتم استعراضها من قبلي',
     'search_permissions_set' => 'حزمة الأذونات',
     'search_created_by_me' => 'أنشئت بواسطتي',
     'search_updated_by_me' => 'حُدثت بواسطتي',
-    'search_date_options' => 'Date Options',
+    'search_date_options' => 'خيارات التاريخ',
     'search_updated_before' => 'حدثت قبل',
     'search_updated_after' => 'حدثت بعد',
     'search_created_before' => 'أنشئت قبل',
@@ -66,22 +67,22 @@ return [
     'search_update' => 'تحديث البحث',
 
     // 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',
+    'shelf' => 'رف',
+    'shelves' => 'الأرفف',
+    'x_shelves' => ':count رف|:count أرفف',
+    'shelves_long' => 'أرفف الكتب',
+    'shelves_empty' => 'لم يتم إنشاء أي أرفف',
+    'shelves_create' => 'إنشاء رف جديد',
     'shelves_popular' => 'Popular Shelves',
-    'shelves_new' => 'New Shelves',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new' => 'أرفف جديدة',
+    'shelves_new_action' => 'رف جديد',
     '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',
+    'shelves_save' => 'حفظ الرف',
     'shelves_books' => 'Books on this shelf',
-    'shelves_add_books' => 'Add books to this shelf',
-    'shelves_drag_books' => 'Drag books here to add them to this shelf',
-    'shelves_empty_contents' => 'This shelf has no books assigned to it',
+    'shelves_add_books' => 'إضافة كتب لهذا الرف',
+    'shelves_drag_books' => 'اسحب الكتب هنا لإضافتها لهذا الرف',
+    'shelves_empty_contents' => 'لا توجد كتب مخصصة لهذا الرف',
     'shelves_edit_and_assign' => 'Edit shelf to assign books',
     'shelves_edit_named' => 'Edit Bookshelf :name',
     'shelves_edit' => 'Edit Bookshelf',
@@ -99,13 +100,13 @@ return [
 
     // Books
     'book' => 'كتاب',
-    'books' => 'كتب',
+    'books' => 'الكتب',
     'x_books' => ':count كتاب|:count كتب',
     'books_empty' => 'لم يتم إنشاء أي كتب',
     'books_popular' => 'كتب رائجة',
     'books_recent' => 'كتب حديثة',
     'books_new' => 'كتب جديدة',
-    'books_new_action' => 'New Book',
+    'books_new_action' => 'كتاب جديد',
     'books_popular_empty' => 'الكتب الأكثر رواجاً ستظهر هنا.',
     'books_new_empty' => 'الكتب المنشأة مؤخراً ستظهر هنا.',
     'books_create' => 'إنشاء كتاب جديد',
@@ -128,8 +129,8 @@ return [
     'books_navigation' => 'تصفح الكتاب',
     'books_sort' => 'فرز محتويات الكتاب',
     'books_sort_named' => 'فرز كتاب :bookName',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
+    'books_sort_name' => 'ترتيب حسب الإسم',
+    'books_sort_created' => 'ترتيب حسب تاريخ الإنشاء',
     'books_sort_updated' => 'Sort by Updated Date',
     'books_sort_chapters_first' => 'Chapters First',
     'books_sort_chapters_last' => 'Chapters Last',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'رفع ملف',
     'attachments_link' => 'إرفاق رابط',
     'attachments_set_link' => 'تحديد الرابط',
-    'attachments_delete_confirm' => 'اضغط على زر الحذف مرة أخرى لتأكيد حذف المرفق.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'أسقط الملفات أو اضغط هنا لإرفاق ملف',
     'attachments_no_files' => 'لم يتم رفع أي ملفات',
     'attachments_explain_link' => 'بالإمكان إرفاق رابط في حال عدم تفضيل رفع ملف. قد يكون الرابط لصفحة أخرى أو لملف في أحد خدمات التخزين السحابي.',
@@ -263,7 +264,8 @@ return [
     'attachment_link' => 'رابط المرفق',
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'رابط الموقع أو الملف',
-    'attach' => 'Attach',
+    'attach' => 'إرفاق',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'تعديل الملف',
     'attachments_edit_file_name' => 'اسم الملف',
     'attachments_edit_drop_upload' => 'أسقط الملفات أو اضغط هنا للرفع والاستبدال',
@@ -273,10 +275,10 @@ return [
     'attachments_file_uploaded' => 'تم رفع الملف بنجاح',
     'attachments_file_updated' => 'تم تحديث الملف بنجاح',
     'attachments_link_attached' => 'تم إرفاق الرابط بالصفحة بنجاح',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
+    'templates' => 'القوالب',
+    'templates_set_as_template' => 'هذه الصفحة عبارة عن قالب',
+    'templates_explain_set_as_template' => 'يمكنك تعيين هذه الصفحة كقالب بحيث تستخدم محتوياتها عند إنشاء صفحات أخرى. سيتمكن المستخدمون الآخرون من استخدام هذا القالب إذا كان لديهم أذونات عرض لهذه الصفحة.',
+    'templates_replace_content' => 'استبدال محتوى الصفحة',
     'templates_append_content' => 'Append to page content',
     'templates_prepend_content' => 'Prepend to page content',
 
@@ -307,8 +309,8 @@ return [
     'comment_in_reply_to' => 'رداً على :commentId',
 
     // 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.'
+    'revision_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف هذا الإصدار؟',
+    'revision_restore_confirm' => 'هل أنت متأكد من أنك تريد استعادة هذا الإصدار؟ سيتم استبدال محتوى الصفحة الحالية.',
+    'revision_delete_success' => 'تم حذف الإصدار',
+    'revision_cannot_delete_latest' => 'لايمكن حذف آخر إصدار.'
 ];
\ No newline at end of file
index 2714d3dbbca7c3aa397e8bc970af0ca245341cb1..cbf1d59583b4223d96881dce944bf6640b897fc7 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'انتهت عملية تحميل الملف.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'لم يتم العثور على المرفق',
 
     // Pages
index 0a689c5d51e961968a83c14c9dd30d6c8a56b4da..3b94cdfc6315e5c64a420d341c39741f49ef33f2 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'الأدوار',
     'role_user_roles' => 'أدوار المستخدمين',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'إدارة إعدادات التطبيق',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'الكل',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
diff --git a/resources/lang/bg/activities.php b/resources/lang/bg/activities.php
new file mode 100644 (file)
index 0000000..a5001bc
--- /dev/null
@@ -0,0 +1,48 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'създадена страница',
+    'page_create_notification'    => 'Страницата беше успешно създадена',
+    'page_update'                 => 'обновена страница',
+    'page_update_notification'    => 'Страницата успешно обновена',
+    'page_delete'                 => 'изтрита страница',
+    'page_delete_notification'    => 'Страницата беше успешно изтрита',
+    'page_restore'                => 'възстановена страница',
+    'page_restore_notification'   => 'Страницата беше успешно възстановена',
+    'page_move'                   => 'преместена страница',
+
+    // Chapters
+    'chapter_create'              => 'създадена страница',
+    'chapter_create_notification' => 'Главата беше успешно създадена',
+    'chapter_update'              => 'обновена глава',
+    'chapter_update_notification' => 'Главата беше успешно обновена',
+    'chapter_delete'              => 'изтрита глава',
+    'chapter_delete_notification' => 'Главата беше успешно изтрита',
+    'chapter_move'                => 'преместена глава',
+
+    // Books
+    'book_create'                 => 'създадена книга',
+    'book_create_notification'    => 'Книгата беше успешно създадена',
+    'book_update'                 => 'обновена книга',
+    'book_update_notification'    => 'Книгата беше успешно обновена',
+    'book_delete'                 => 'изтрита книга',
+    'book_delete_notification'    => 'Книгата беше успешно изтрита',
+    'book_sort'                   => 'сортирана книга',
+    'book_sort_notification'      => 'Книгата беше успешно преподредена',
+
+    // Bookshelves
+    'bookshelf_create'            => 'създаден рафт',
+    'bookshelf_create_notification'    => 'Рафтът беше успешно създаден',
+    'bookshelf_update'                 => 'обновен рафт',
+    'bookshelf_update_notification'    => 'Рафтът беше успешно обновен',
+    'bookshelf_delete'                 => 'изтрит рафт',
+    'bookshelf_delete_notification'    => 'Рафтът беше успешно изтрит',
+
+    // Other
+    'commented_on'                => 'коментирано на',
+];
diff --git a/resources/lang/bg/auth.php b/resources/lang/bg/auth.php
new file mode 100644 (file)
index 0000000..018e871
--- /dev/null
@@ -0,0 +1,77 @@
+<?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' => 'Въведените удостоверителни данни не съвпадат с нашите записи.',
+    'throttle' => 'Твърде много опити за влизане. Опитайте пак след :seconds секунди.',
+
+    // Login & Register
+    'sign_up' => 'Регистриране',
+    'log_in' => 'Влизане',
+    'log_in_with' => 'Влизане с :socialDriver',
+    'sign_up_with' => 'Регистриране с :socialDriver',
+    'logout' => 'Изход',
+
+    'name' => 'Име',
+    'username' => 'Потребител',
+    'email' => 'Имейл',
+    'password' => 'Парола',
+    'password_confirm' => 'Потвърди паролата',
+    'password_hint' => 'Трябва да бъде поне 7 символа',
+    'forgot_password' => 'Забравена парола?',
+    'remember_me' => 'Запомни ме',
+    'ldap_email_hint' => 'Моля въведете емейл, който да използвате за дадения акаунт.',
+    'create_account' => 'Създай Акаунт',
+    'already_have_account' => 'Вече имате акаунт?',
+    'dont_have_account' => 'Нямате акаунт?',
+    'social_login' => 'Влизане по друг начин',
+    'social_registration' => 'Регистрация по друг начин',
+    'social_registration_text' => 'Регистрация и влизане използвайки друг начин.',
+
+    'register_thanks' => 'Благодарим Ви за регистрацията!',
+    'register_confirm' => 'Моля проверете своя емейл и натиснете върху бутона за потвърждение, за да влезете в :appName.',
+    'registrations_disabled' => 'Регистрациите към момента са забранени',
+    'registration_email_domain_invalid' => 'Този емейл домейн към момента няма достъп до приложението',
+    'register_success' => 'Благодарим Ви за регистрацията! В момента сте регистриран и сте вписани в приложението.',
+
+
+    // Password Reset
+    'reset_password' => 'Нулиране на паролата',
+    'reset_password_send_instructions' => 'Въведете емейла си и ще ви бъде изпратен емейл с линк за нулиране на паролата.',
+    'reset_password_send_button' => 'Изпращане на линк за нулиране',
+    'reset_password_sent' => 'Линк за нулиране на паролата ще Ви бъде изпратен на :email, ако емейлът Ви бъде открит в системата.',
+    'reset_password_success' => 'Паролата Ви е променена успешно.',
+    'email_reset_subject' => 'Възстановете паролата си за :appName',
+    'email_reset_text' => 'Вие получихте този емейл, защото поискахте вашата парола да бъде занулена.',
+    'email_reset_not_requested' => 'Ако Вие не сте поискали зануляването на паролата, няма нужда от други действия.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Потвърди емейла си за :appName',
+    'email_confirm_greeting' => 'Благодарим Ви, че се присъединихте към :appName!',
+    'email_confirm_text' => 'Моля, потвърдете вашия имейл адрес, като следвате връзката по-долу:',
+    'email_confirm_action' => 'Потвърдете имейл',
+    'email_confirm_send_error' => 'Нужно ви е потвърждение чрез емейл, но системата не успя да го изпрати. Моля свържете се с администратора, за да проверите дали вашият емейл адрес е конфигуриран правилно.',
+    'email_confirm_success' => 'Адресът на електронната ви поща е потвърден!',
+    'email_confirm_resent' => 'Беше изпратен имейл с потвърждение, Моля, проверете кутията си.',
+
+    'email_not_confirmed' => 'Имейл адресът не е потвърден',
+    'email_not_confirmed_text' => 'Вашият емейл адрес все още не е потвърден.',
+    'email_not_confirmed_click_link' => 'Моля да последвате линка, който ви беше изпратен непосредствено след регистрацията.',
+    'email_not_confirmed_resend' => 'Ако не откривате писмото, може да го изпратите отново като попълните формуляра по-долу.',
+    'email_not_confirmed_resend_button' => 'Изпрати отново емейла за потвърждение',
+
+    // User Invite
+    'user_invite_email_subject' => 'Вие бяхте поканен да се присъедините към :appName!',
+    'user_invite_email_greeting' => 'Беше създаден акаунт за Вас във :appName.',
+    'user_invite_email_text' => 'Натисните бутона по-долу за да определите парола и да получите достъп:',
+    'user_invite_email_action' => 'Парола на акаунта',
+    'user_invite_page_welcome' => 'Добре дошли в :appName!',
+    'user_invite_page_text' => 'За да финализирате вашият акаунт и да получите достъп трябва да определите парола, която да бъде използвана за следващия влизания в :appName.',
+    'user_invite_page_confirm_button' => 'Потвърди паролата',
+    'user_invite_success' => 'Паролата е потвърдена и вече имате достъп до :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/bg/common.php b/resources/lang/bg/common.php
new file mode 100644 (file)
index 0000000..99930dd
--- /dev/null
@@ -0,0 +1,80 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Отказ',
+    'confirm' => 'Потвърди',
+    'back' => 'Назад',
+    'save' => 'Запази',
+    'continue' => 'Продължи',
+    'select' => 'Избери',
+    'toggle_all' => 'Избери всички',
+    'more' => 'Повече',
+
+    // Form Labels
+    'name' => 'Име',
+    'description' => 'Описание',
+    'role' => 'Роля',
+    'cover_image' => 'Основно изображение',
+    'cover_image_description' => 'Картината трябва да е приблизително 440х250 пиксела.',
+    
+    // Actions
+    'actions' => 'Действия',
+    'view' => 'Преглед',
+    'view_all' => 'Преглед на всички',
+    'create' => 'Създай',
+    'update' => 'Обновяване',
+    'edit' => 'Редактиране',
+    'sort' => 'Сортиране',
+    'move' => 'Преместване',
+    'copy' => 'Копирай',
+    'reply' => 'Отговори',
+    'delete' => 'Изтрий',
+    'delete_confirm' => 'Confirm Deletion',
+    'search' => 'Търси',
+    'search_clear' => 'Изчисти търсенето',
+    'reset' => 'Нулирай',
+    'remove' => 'Премахване',
+    'add' => 'Добави',
+    'fullscreen' => 'Пълен екран',
+
+    // Sort Options
+    'sort_options' => 'Опции за сортиране',
+    'sort_direction_toggle' => 'Активирай сортиране',
+    'sort_ascending' => 'Сортирай възходящо',
+    'sort_descending' => 'Низходящо сортиране',
+    'sort_name' => 'Име',
+    'sort_created_at' => 'Дата на създаване',
+    'sort_updated_at' => 'Дата на обновяване',
+
+    // Misc
+    'deleted_user' => 'Изтриване на потребител',
+    'no_activity' => 'Няма активност за показване',
+    'no_items' => 'Няма налични артикули',
+    'back_to_top' => 'Върнете се в началото',
+    'toggle_details' => 'Активирай детайли',
+    'toggle_thumbnails' => 'Активирай миниатюри',
+    'details' => 'Подробности',
+    'grid_view' => 'Табличен изглед',
+    'list_view' => 'Изглед списък',
+    'default' => 'Основен',
+    'breadcrumb' => 'Трасиране',
+
+    // Header
+    'profile_menu' => 'Профил меню',
+    'view_profile' => 'Разглеждане на профил',
+    'edit_profile' => 'Редактиране на профила',
+    'dark_mode' => 'Тъмен режим',
+    'light_mode' => 'Светъл режим',
+
+    // Layout tabs
+    'tab_info' => 'Информация',
+    'tab_content' => 'Съдържание',
+
+    // Email Content
+    'email_action_help' => 'Ако имате проблеми с бутона ":actionText" по-горе, копирайте и поставете URL адреса по-долу в уеб браузъра си:',
+    'email_rights' => 'Всички права запазени',
+];
diff --git a/resources/lang/bg/components.php b/resources/lang/bg/components.php
new file mode 100644 (file)
index 0000000..b15aa6f
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Избор на изображение',
+    'image_all' => 'Всички',
+    'image_all_title' => 'Преглед на всички изображения',
+    'image_book_title' => 'Виж изображенията прикачени към тази книга',
+    'image_page_title' => 'Виж изображенията прикачени към страницата',
+    'image_search_hint' => 'Търси по име на картина',
+    'image_uploaded' => 'Качено :uploadedDate',
+    'image_load_more' => 'Зареди повече',
+    'image_image_name' => 'Име на изображението',
+    'image_delete_used' => 'Това изображение е използвано в страницата по-долу.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Изберете изображение',
+    'image_dropzone' => 'Поставете тук изображение или кликнете тук за да качите',
+    'images_deleted' => 'Изображението е изтрито',
+    'image_preview' => 'Преглед на изображенията',
+    'image_upload_success' => 'Изображението бе качено успешно',
+    'image_update_success' => 'Данните за изобтажението са обновенни успешно',
+    'image_delete_success' => 'Изображението е успешно изтрито',
+    'image_upload_remove' => 'Премахване',
+
+    // Code Editor
+    'code_editor' => 'Редактиране на кода',
+    'code_language' => 'Език на кода',
+    'code_content' => 'Съдържание на кода',
+    'code_session_history' => 'Session History',
+    'code_save' => 'Запази кода',
+];
diff --git a/resources/lang/bg/entities.php b/resources/lang/bg/entities.php
new file mode 100644 (file)
index 0000000..796bcd9
--- /dev/null
@@ -0,0 +1,316 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Наскоро създадени',
+    'recently_created_pages' => 'Наскоро създадени страници',
+    'recently_updated_pages' => 'Наскоро актуализирани страници',
+    'recently_created_chapters' => 'Наскоро създадени глави',
+    'recently_created_books' => 'Наскоро създадени книги',
+    'recently_created_shelves' => 'Наскоро създадени рафтове',
+    'recently_update' => 'Наскоро актуализирани',
+    'recently_viewed' => 'Скорошно разгледани',
+    'recent_activity' => 'Последна активност',
+    'create_now' => 'Създай една сега',
+    'revisions' => 'Ревизии',
+    'meta_revision' => 'Ревизия #:revisionCount',
+    'meta_created' => 'Създадено преди :timeLength',
+    'meta_created_name' => 'Създадено преди :timeLength от :user',
+    'meta_updated' => 'Актуализирано :timeLength',
+    'meta_updated_name' => 'Актуализирано преди :timeLength от :user',
+    'entity_select' => 'Избор на обект',
+    'images' => 'Изображения',
+    'my_recent_drafts' => 'Моите скорошни драфтове',
+    'my_recently_viewed' => 'Моите скорошни преглеждания',
+    'no_pages_viewed' => 'Не сте прегледали никакви страници',
+    'no_pages_recently_created' => 'Не са били създавани страници скоро',
+    'no_pages_recently_updated' => 'Не са били актуализирани страници скоро',
+    'export' => 'Експортиране',
+    'export_html' => 'Прикачени уеб файлове',
+    'export_pdf' => 'PDF файл',
+    'export_text' => 'Обикновен текстов файл',
+
+    // Permissions and restrictions
+    'permissions' => 'Права',
+    'permissions_intro' => 'Веднъж добавени, тези права ще вземат приоритет над всички други установени права.',
+    'permissions_enable' => 'Разреши уникални права',
+    'permissions_save' => 'Запази права',
+
+    // Search
+    'search_results' => 'Резултати от търсенето',
+    'search_total_results_found' => ':count резултати намерени|:count общо намерени резултати',
+    'search_clear' => 'Изчисти търсенето',
+    'search_no_pages' => 'Няма страници отговарящи на търсенето',
+    'search_for_term' => 'Търси :term',
+    'search_more' => 'Още резултати',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
+    'search_content_type' => 'Тип на съдържание',
+    'search_exact_matches' => 'Точни съвпадения',
+    'search_tags' => 'Търсене на тагове',
+    'search_options' => 'Настройки',
+    'search_viewed_by_me' => 'Прегледано от мен',
+    'search_not_viewed_by_me' => 'Непрегледано от мен',
+    'search_permissions_set' => 'Задаване на права',
+    'search_created_by_me' => 'Създадено от мен',
+    'search_updated_by_me' => 'Обновено от мен',
+    'search_date_options' => 'Настройки на дати',
+    'search_updated_before' => 'Обновено преди',
+    'search_updated_after' => 'Обновено след',
+    'search_created_before' => 'Създадено преди',
+    'search_created_after' => 'Създадено след',
+    'search_set_date' => 'Задаване на дата',
+    'search_update' => 'Обнови търсенето',
+
+    // Shelves
+    'shelf' => 'Рафт',
+    'shelves' => 'Рафтове',
+    'x_shelves' => ':count Рафт|:count Рафтове',
+    'shelves_long' => 'Рафтове с книги',
+    'shelves_empty' => 'Няма създадени рафтове',
+    'shelves_create' => 'Създай нов рафт',
+    'shelves_popular' => 'Популярни рафтове',
+    'shelves_new' => 'Нови рафтове',
+    'shelves_new_action' => 'Нов рафт',
+    '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',
+
+    // Books
+    'book' => 'Книга',
+    'books' => 'Книги',
+    'x_books' => ':count Книга|:count Книги',
+    'books_empty' => 'Няма създадени книги',
+    'books_popular' => 'Популярни книги',
+    'books_recent' => 'Скоро разглеждани книги',
+    'books_new' => 'Нови книги',
+    'books_new_action' => 'Нова книга',
+    'books_popular_empty' => 'Най-популярните книги ще излязат тук.',
+    'books_new_empty' => 'Най-новите книги ще излязат тук.',
+    'books_create' => 'Създай нова книга',
+    'books_delete' => 'Изтрита книга',
+    'books_delete_named' => 'Изтрий книга :bookName',
+    'books_delete_explain' => 'Това действие ще изтрие книга с името \':bookName\'. Всички страници и глави ще бъдат изтрити.',
+    'books_delete_confirmation' => 'Сигурен ли сте, че искате да изтриете книгата?',
+    'books_edit' => 'Редактиране на книга',
+    'books_edit_named' => 'Редактирай книга :bookName',
+    'books_form_book_name' => 'Име на книга',
+    'books_save' => 'Запази книга',
+    'books_permissions' => 'Настройки за достъп до книгата',
+    'books_permissions_updated' => 'Настройките за достъп до книгата бяха обновени',
+    'books_empty_contents' => 'Няма създадени страници или глави към тази книга.',
+    'books_empty_create_page' => 'Създаване на нова страница',
+    'books_empty_sort_current_book' => 'Сортирай настоящата книга',
+    'books_empty_add_chapter' => 'Добавяне на раздел',
+    'books_permissions_active' => 'Настройките за достъп до книгата са активни',
+    'books_search_this' => 'Търси в книгата',
+    'books_navigation' => 'Навигация на книгата',
+    'books_sort' => 'Сортирай съдържанието на книгата',
+    'books_sort_named' => 'Сортирай книга :bookName',
+    'books_sort_name' => 'Сортиране по име',
+    'books_sort_created' => 'Сортирай по дата на създаване',
+    'books_sort_updated' => 'Сортирай по дата на обновяване',
+    'books_sort_chapters_first' => 'Първа глава',
+    'books_sort_chapters_last' => 'Последна глава',
+    'books_sort_show_other' => 'Покажи други книги',
+    'books_sort_save' => 'Запази новата подредба',
+
+    // Chapters
+    'chapter' => 'Глава',
+    'chapters' => 'Глави',
+    'x_chapters' => ':count Глава|:count Глави',
+    'chapters_popular' => 'Популярни глави',
+    'chapters_new' => 'Нова глава',
+    'chapters_create' => 'Създай нова глава',
+    'chapters_delete' => 'Изтрий глава',
+    'chapters_delete_named' => 'Изтрий глава :chapterName',
+    'chapters_delete_explain' => 'Ще бъде изтрита глава с име \':chapterName\'. Всички страници в нея ще бъдат премахнати и добавени в основната книга.',
+    'chapters_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази глава?',
+    'chapters_edit' => 'Редактирай глава',
+    'chapters_edit_named' => 'Актуализирай глава :chapterName',
+    'chapters_save' => 'Запази глава',
+    'chapters_move' => 'Премести глава',
+    'chapters_move_named' => 'Премести глава :chapterName',
+    'chapter_move_success' => 'Главата беше преместена в :bookName',
+    'chapters_permissions' => 'Настойки за достъп на главата',
+    'chapters_empty' => 'Няма създадени страници в тази глава.',
+    'chapters_permissions_active' => 'Настройките за достъп до глава са активни',
+    'chapters_permissions_success' => 'Настройките за достъп до главата бяха обновени',
+    'chapters_search_this' => 'Търси в тази глава',
+
+    // Pages
+    'page' => 'Страница',
+    'pages' => 'Страници',
+    'x_pages' => ':count Страница|:count Страници',
+    'pages_popular' => 'Популярни страници',
+    'pages_new' => 'Нова страница',
+    'pages_attachments' => 'Прикачени файлове',
+    'pages_navigation' => 'Навигация на страница',
+    'pages_delete' => 'Изтрий страница',
+    'pages_delete_named' => 'Изтрий страница :pageName',
+    'pages_delete_draft_named' => 'Изтрий чернова :pageName',
+    'pages_delete_draft' => 'Изтрий чернова',
+    'pages_delete_success' => 'Страницата е изтрита',
+    'pages_delete_draft_success' => 'Черновата на страницата бе изтрита',
+    'pages_delete_confirm' => 'Сигурни ли сте, че искате да изтриете тази страница?',
+    'pages_delete_draft_confirm' => 'Сигурни ли сте, че искате да изтриете тази чернова?',
+    'pages_editing_named' => 'Редактиране на страница :pageName',
+    'pages_edit_draft_options' => 'Настройки на черновата',
+    'pages_edit_save_draft' => 'Запазване на чернова',
+    'pages_edit_draft' => 'Редактирай на черновата',
+    'pages_editing_draft' => 'Редактиране на чернова',
+    'pages_editing_page' => 'Редактиране на страница',
+    'pages_edit_draft_save_at' => 'Черновата е запазена в ',
+    'pages_edit_delete_draft' => 'Изтрий чернова',
+    'pages_edit_discard_draft' => 'Отхвърляне на черновата',
+    'pages_edit_set_changelog' => 'Задайте регистър на промените',
+    'pages_edit_enter_changelog_desc' => 'Въведете кратко резюме на промените, които сте създали',
+    'pages_edit_enter_changelog' => 'Въведи регистър на промените',
+    'pages_save' => 'Запазване на страницата',
+    'pages_title' => 'Заглавие на страницата',
+    'pages_name' => 'Име на страницата',
+    'pages_md_editor' => 'Редактор',
+    'pages_md_preview' => 'Предварителен преглед',
+    'pages_md_insert_image' => 'Добавяна на изображение',
+    'pages_md_insert_link' => 'Добави линк към обекта',
+    'pages_md_insert_drawing' => 'Вмъкни рисунка',
+    'pages_not_in_chapter' => 'Страницата не принадлежи в никоя глава',
+    'pages_move' => 'Премести страницата',
+    'pages_move_success' => 'Страницата беше преместена в ":parentName"',
+    'pages_copy' => 'Копиране на страницата',
+    'pages_copy_desination' => 'Копиране на дестинацията',
+    'pages_copy_success' => 'Страницата беше успешно копирана',
+    'pages_permissions' => 'Настройки за достъп на страницата',
+    'pages_permissions_success' => 'Настройките за достъп до страницата бяха обновени',
+    'pages_revision' => 'Ревизия',
+    'pages_revisions' => 'Ревизии на страницата',
+    'pages_revisions_named' => 'Ревизии на страницата :pageName',
+    'pages_revision_named' => 'Ревизия на страницата :pageName',
+    'pages_revisions_created_by' => 'Създадено от',
+    'pages_revisions_date' => 'Дата на ревизията',
+    'pages_revisions_number' => '№',
+    'pages_revisions_numbered' => 'Ревизия №:id',
+    'pages_revisions_numbered_changes' => 'Ревизия №:id Промени',
+    'pages_revisions_changelog' => 'История на промените',
+    'pages_revisions_changes' => 'Промени',
+    'pages_revisions_current' => 'Текуща версия',
+    'pages_revisions_preview' => 'Предварителен преглед',
+    'pages_revisions_restore' => 'Възстановяване',
+    'pages_revisions_none' => 'Тази страница няма ревизии',
+    'pages_copy_link' => 'Копирай връзката',
+    'pages_edit_content_link' => 'Редактиране на съдържанието',
+    'pages_permissions_active' => 'Настройките за достъп до страницата са активни',
+    'pages_initial_revision' => 'Първо публикуване',
+    'pages_initial_name' => 'Нова страница',
+    'pages_editing_draft_notification' => 'В момента редактирате чернова, която беше последно обновена :timeDiff.',
+    'pages_draft_edited_notification' => 'Тази страница беше актуализирана от тогава. Препоръчително е да изтриете настоящата чернова.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count потребителя започнаха да редактират настоящата страница',
+        'start_b' => ':userName в момента редактира тази страница',
+        'time_a' => 'от както страницата беше актуализирана',
+        'time_b' => 'в последните :minCount минути',
+        'message' => ':start :time. Внимавайте да не попречите на актуализацията на другия!',
+    ],
+    'pages_draft_discarded' => 'Черновата беше отхърлена, Редактора беше обновен с актуалното съдържание на страницата',
+    'pages_specific' => 'Определена страница',
+    'pages_is_template' => 'Шаблон на страницата',
+
+    // Editor Sidebar
+    'page_tags' => 'Тагове на страницата',
+    'chapter_tags' => 'Тагове на главата',
+    'book_tags' => 'Тагове на книгата',
+    'shelf_tags' => 'Тагове на рафта',
+    'tag' => 'Таг',
+    'tags' =>  'Тагове',
+    'tag_name' =>  'Име на таг',
+    'tag_value' => 'Съдържание на тага (Опционално)',
+    'tags_explain' => "Добавете няколко тага за да категоризирате по добре вашето съдържание. \n Може да добавите съдържание на таговете за по-подробна организация.",
+    'tags_add' => 'Добави друг таг',
+    'tags_remove' => 'Премахни този таг',
+    'attachments' => 'Прикачени файлове',
+    'attachments_explain' => 'Прикачете файлове или линкове, които да са видими на вашата страница. Същите ще бъдат видими във вашето странично поле.',
+    'attachments_explain_instant_save' => 'Промените тук се запазват веднага.',
+    'attachments_items' => 'Прикачен файл',
+    'attachments_upload' => 'Прикачен файл',
+    'attachments_link' => 'Прикачване на линк',
+    'attachments_set_link' => 'Поставяне на линк',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_dropzone' => 'Поставете файлове или цъкнете тук за да прикачите файл',
+    'attachments_no_files' => 'Няма прикачени фалове',
+    'attachments_explain_link' => 'Може да прикачите линк, ако не искате да качвате файл. Този линк може да бъде към друга страница или към файл в облакова пространство.',
+    'attachments_link_name' => 'Има на линка',
+    'attachment_link' => 'Линк към прикачения файл',
+    'attachments_link_url' => 'Линк към файла',
+    'attachments_link_url_hint' => 'Url на сайт или файл',
+    'attach' => 'Прикачване',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
+    'attachments_edit_file' => 'Редактирай файл',
+    'attachments_edit_file_name' => 'Име на файл',
+    'attachments_edit_drop_upload' => 'Поставете файл или цъкнете тук за да прикачите и обновите',
+    'attachments_order_updated' => 'Прикачения файл беше обновен',
+    'attachments_updated_success' => 'Данните на прикачения файл бяха обновени',
+    'attachments_deleted' => 'Прикачения файл беше изтрит',
+    'attachments_file_uploaded' => 'Файлът беше качен успешно',
+    'attachments_file_updated' => 'Файлът беше обновен успешно',
+    'attachments_link_attached' => 'Линкът беше успешно прикачен към страницата',
+    'templates' => 'Шаблони',
+    'templates_set_as_template' => 'Страницата е шаблон',
+    'templates_explain_set_as_template' => 'Можете да зададете тази страница като шаблон, така че нейното съдържание да бъде използвано при създаването на други страници. Други потребители ще могат да използват този шаблон, ако имат разрешения за преглед на тази страница.',
+    'templates_replace_content' => 'Замени съдържанието на страницата',
+    'templates_append_content' => 'Добави в края на съдържанието на страницата',
+    'templates_prepend_content' => 'Добави в началото на съдържанието на страницата',
+
+    // Profile View
+    'profile_user_for_x' => 'Потребител от :time',
+    'profile_created_content' => 'Създадено съдържание',
+    'profile_not_created_pages' => ':userName не е създал страници',
+    'profile_not_created_chapters' => ':userName не е създавал глави',
+    'profile_not_created_books' => ':userName не е създавал книги',
+    'profile_not_created_shelves' => ':userName не е създавал рафтове',
+
+    // Comments
+    'comment' => 'Коментирай',
+    'comments' => 'Коментари',
+    'comment_add' => 'Добавяне на коментар',
+    'comment_placeholder' => 'Напишете коментар',
+    'comment_count' => '{0} Няма коментари|{1} 1 коментар|[2,*] :count коментара',
+    'comment_save' => 'Запази коментар',
+    'comment_saving' => 'Запазване на коментар...',
+    'comment_deleting' => 'Изтриване на коментар...',
+    'comment_new' => 'Нов коментар',
+    'comment_created' => 'коментирано :createDiff',
+    'comment_updated' => 'Актуализирано :updateDiff от :username',
+    'comment_deleted_success' => 'Коментарът е изтрит',
+    'comment_created_success' => 'Коментарът е добавен',
+    'comment_updated_success' => 'Коментарът е обновен',
+    'comment_delete_confirm' => 'Наистина ли искате да изтриете този коментар?',
+    'comment_in_reply_to' => 'В отговор на :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Наистина ли искате да изтриете тази версия?',
+    'revision_restore_confirm' => 'Сигурни ли сте, че искате да изтриете тази версия? Настоящата страница ще бъде заместена.',
+    'revision_delete_success' => 'Версията беше изтрита',
+    'revision_cannot_delete_latest' => 'Не може да изтриете последната версия.'
+];
\ No newline at end of file
diff --git a/resources/lang/bg/errors.php b/resources/lang/bg/errors.php
new file mode 100644 (file)
index 0000000..4b062af
--- /dev/null
@@ -0,0 +1,102 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Нямате права за достъп до избраната страница.',
+    'permissionJson' => 'Нямате права да извършите тази операция.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Потребител с емайл :email вече съществува но с други данни.',
+    'email_already_confirmed' => 'Емейлът вече беше потвърден. Моля опитрайте да влезете.',
+    'email_confirmation_invalid' => 'Този код за достъп не е валиден или вече е бил използван, Моля опитрайте се да се регистрирате отново.',
+    'email_confirmation_expired' => 'Кодът за потвърждение изтече, нов емейл за потвърждение беше изпратен.',
+    'email_confirmation_awaiting' => 'Емайл адреса, който използвате трябва да се потвърди',
+    'ldap_fail_anonymous' => 'LDAP протокола прекъсна, използвайки анонимни настройки',
+    'ldap_fail_authed' => 'Опита за достъп чрез LDAP с използваната парола не беше успешен',
+    'ldap_extension_not_installed' => 'LDAP PHP не беше инсталирана',
+    'ldap_cannot_connect' => 'Не може да се свържете с Ldap сървъра, първоначалната връзка се разпадна',
+    'saml_already_logged_in' => 'Вече сте влезли',
+    'saml_user_not_registered' => 'Потребителят :name не е регистриран и автоматичната регистрация не е достъпна',
+    'saml_no_email_address' => 'Не успяхме да намерим емейл адрес, за този потребител, от информацията предоставена от външната система',
+    'saml_invalid_response_id' => 'Заявката от външната система не е разпознат от процеса започнат от това приложение. Връщането назад след влизане може да породи този проблем.',
+    'saml_fail_authed' => 'Влизането чрез :system не беше успешно, системата не успя да оторизира потребителя',
+    'social_no_action_defined' => 'Действието не беше дефинирано',
+    'social_login_bad_response' => "Възникна грешка по време на :socialAccount login: \n:error",
+    'social_account_in_use' => 'Този :socialAccount вече е използван. Опитайте се да влезете чрез опцията за :socialAccount.',
+    'social_account_email_in_use' => 'Този емейл адрес вече е бил използван. Ако вече имате профил, може да го свържете чрез :socialAccount от вашия профил.',
+    'social_account_existing' => 'Този :socialAccount вече в свързан с вашия профил.',
+    'social_account_already_used_existing' => 'Този :socialAccount вече се използва от друг потребител.',
+    'social_account_not_used' => 'Този :socialAccount не е свързан с профил. Моля свържете го с вашия профил. ',
+    'social_account_register_instructions' => 'Ако все още нямате профил, може да се регистрирате чрез :socialAccount опцията.',
+    'social_driver_not_found' => 'Social driver not found',
+    'social_driver_not_configured' => 'Your :socialAccount social settings are not configured correctly.',
+    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+
+    // System
+    'path_not_writable' => 'File path :filePath could not be uploaded to. Ensure it is writable to the server.',
+    'cannot_get_image_from_url' => 'Cannot get image from :url',
+    'cannot_create_thumbs' => 'The server cannot create thumbnails. Please check you have the GD PHP extension installed.',
+    'server_upload_limit' => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'uploaded'  => 'The server does not allow uploads of this size. Please try a smaller file size.',
+    'image_upload_error' => 'An error occurred uploading the image',
+    'image_upload_type_error' => 'The image type being uploaded is invalid',
+    'file_upload_timeout' => 'The file upload has timed out.',
+
+    // Attachments
+    'attachment_not_found' => 'Attachment not found',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Failed to save draft. Ensure you have internet connection before saving this page',
+    'page_custom_home_deletion' => 'Cannot delete a page while it is set as a homepage',
+
+    // Entities
+    'entity_not_found' => 'Entity not found',
+    'bookshelf_not_found' => 'Bookshelf not found',
+    'book_not_found' => 'Book not found',
+    'page_not_found' => 'Page not found',
+    'chapter_not_found' => 'Chapter not found',
+    'selected_book_not_found' => 'The selected book was not found',
+    'selected_book_chapter_not_found' => 'The selected Book or Chapter was not found',
+    'guests_cannot_save_drafts' => 'Guests cannot save drafts',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
+    'users_cannot_delete_guest' => 'You cannot delete the guest user',
+
+    // Roles
+    'role_cannot_be_edited' => 'This role cannot be edited',
+    'role_system_cannot_be_deleted' => 'This role is a system role and cannot be deleted',
+    'role_registration_default_cannot_delete' => 'This role cannot be deleted while set as the default registration role',
+    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+
+    // Comments
+    'comment_list' => 'An error occurred while fetching the comments.',
+    'cannot_add_comment_to_draft' => 'Не може да добавяте коментари към чернова.',
+    'comment_add' => 'Възникна грешка при актуализиране/добавяне на коментар.',
+    'comment_delete' => 'Възникна грешка при изтриването на коментара.',
+    'empty_comment' => 'Не може да добавите празен коментар.',
+
+    // Error pages
+    '404_page_not_found' => 'Страницата не е намерена',
+    'sorry_page_not_found' => 'Страницата, която търсите не може да бъде намерена.',
+    'sorry_page_not_found_permission_warning' => 'Ако смятате, че тази страница съществува, най-вероятно нямате право да я преглеждате.',
+    'return_home' => 'Назад към Начало',
+    'error_occurred' => 'Възникна грешка',
+    'app_down' => ':appName не е достъпно в момента',
+    'back_soon' => 'Ще се върне обратно онлайн скоро.',
+
+    // API errors
+    'api_no_authorization_found' => 'Но беше намерен код за достъп в заявката',
+    'api_bad_authorization_format' => 'В заявката имаше код за достъп, но формата изглежда е неправилен',
+    'api_user_token_not_found' => 'Няма открит API код, който да отговоря на предоставения такъв',
+    'api_incorrect_token_secret' => 'Секретния код, който беше предоставен за достъп до API-а е неправилен',
+    'api_user_no_api_permission' => 'Собственика на АPI кода няма право да прави API заявки',
+    'api_user_token_expired' => 'Кода за достъп, който беше използван, вече не е валиден',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Беше върната грешка, когато се изпрати тестовият емейл:',
+
+];
diff --git a/resources/lang/bg/pagination.php b/resources/lang/bg/pagination.php
new file mode 100644 (file)
index 0000000..7844171
--- /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; Предишна',
+    'next'     => 'Следваща &raquo;',
+
+];
diff --git a/resources/lang/bg/passwords.php b/resources/lang/bg/passwords.php
new file mode 100644 (file)
index 0000000..871fdee
--- /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' => 'Паролите трябва да имат поне 8 символа и да съвпадат с потвърждението.',
+    'user' => "Не можем да намерим потребител с този имейл адрес.",
+    'token' => 'Кодът за зануляване на паролата е невалиден за този емейл адрес.',
+    'sent' => 'Пратихме връзка за нулиране на паролата до имейла ви!',
+    'reset' => 'Вашата парола е нулирана!',
+
+];
diff --git a/resources/lang/bg/settings.php b/resources/lang/bg/settings.php
new file mode 100644 (file)
index 0000000..e8612c3
--- /dev/null
@@ -0,0 +1,230 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Настройки',
+    'settings_save' => 'Запази настройките',
+    'settings_save_success' => 'Настройките са записани',
+
+    // App Settings
+    'app_customization' => 'Персонализиране',
+    'app_features_security' => 'Екстри и Сигурност',
+    'app_name' => 'Име на приложението',
+    'app_name_desc' => 'Това име е включено във всяка шапка и във всеки имейл изпратен от системата.',
+    'app_name_header' => 'Покажи името в шапката',
+    'app_public_access' => 'Публичен достъп',
+    'app_public_access_desc' => 'Активирането на тази настройка, ще позволи на гости, които не са влезли в системта, да имат достъп до съдържанието на вашето приложение.',
+    'app_public_access_desc_guest' => 'Достъпа на гостите може да бъде контролиран от "Guest" потребителя.',
+    'app_public_access_toggle' => 'Позволяване на публичен достъп',
+    'app_public_viewing' => 'Позволване на публичен достъп?',
+    'app_secure_images' => 'По-висока сигурност при качване на изображения',
+    'app_secure_images_toggle' => 'Активиране на по-висока сигурност при качване на изображения',
+    'app_secure_images_desc' => 'С цел производителност, всички изображения са публични. Тази настройка добавя случаен, труден за отгатване низ от символи пред линка на изображението. Подсигурете, че индексите на директорията не са включени за да предотвратите лесен достъп.',
+    'app_editor' => 'Редактор на страница',
+    'app_editor_desc' => 'Изберете кой редактор да се използва от всички потребители за да редактират страници.',
+    'app_custom_html' => 'Персонализирано съдържание на HTML шапката',
+    'app_custom_html_desc' => 'Всяко съдържание, добавено тук, ще бъде поставено в долната част на секцията <head> на всяка страница. Това е удобно за преобладаващи стилове или добавяне на код за анализ.',
+    'app_custom_html_disabled_notice' => 'Съдържанието на персонализираната HTML шапка е деактивирано на страницата с настройки, за да се гарантира, че евентуални лоши промени могат да бъдат върнати.',
+    'app_logo' => 'Лого на приложението',
+    'app_logo_desc' => 'Това изображение трябва да е с 43px височина. <br> Големите изображения ще бъдат намалени.',
+    'app_primary_color' => 'Основен цвят на приложението',
+    'app_primary_color_desc' => 'Изберете основния цвят на приложението, включително на банера, бутоните и линковете.',
+    '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_toggle' => 'Disable comments',
+    'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
+
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
+    // Registration Settings
+    '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_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    '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',
+
+    // Maintenance settings
+    'maint' => 'Maintenance',
+    'maint_image_cleanup' => 'Cleanup Images',
+    'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
+    'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
+    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
+    'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
+    'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
+    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
+    'maint_send_test_email_run' => 'Send test email',
+    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
+    'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
+
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
+    // Role Settings
+    'roles' => 'Roles',
+    'role_user_roles' => 'User Roles',
+    'role_create' => 'Create New Role',
+    'role_create_success' => 'Role successfully created',
+    'role_delete' => 'Delete Role',
+    'role_delete_confirm' => 'This will delete the role with the name \':roleName\'.',
+    'role_delete_users_assigned' => 'This role has :userCount users assigned to it. If you would like to migrate the users from this role select a new role below.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Are you sure you want to delete this role?',
+    'role_delete_success' => 'Role successfully deleted',
+    'role_edit' => 'Редактиране на роля',
+    'role_details' => 'Детайли на роля',
+    'role_name' => 'Име на ролята',
+    'role_desc' => 'Кратко описание на ролята',
+    'role_external_auth_id' => 'Външни ауторизиращи ID-a',
+    'role_system' => 'Настойки за достъп на системата',
+    'role_manage_users' => 'Управление на потребители',
+    'role_manage_roles' => 'Управление роли и права',
+    'role_manage_entity_permissions' => 'Управление на правата за достъп всички книги, глави и страници',
+    'role_manage_own_entity_permissions' => 'Управление на правата за достъп на собствени книги, глави и страници',
+    'role_manage_page_templates' => 'Управление на шаблони на страници',
+    'role_access_api' => 'Достъп до API на системата',
+    'role_manage_settings' => 'Управление на настройките на приложението',
+    'role_asset' => 'Настройки за достъп до активи',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
+    'role_asset_desc' => 'Тези настойки за достъп контролират достъпа по подразбиране до активите в системата. Настойките за достъп до книги, глави и страници ще отменят тези настройки.',
+    'role_asset_admins' => 'Администраторите автоматично получават достъп до цялото съдържание, но тези опции могат да показват или скриват опциите за потребителския интерфейс.',
+    'role_all' => 'Всички',
+    'role_own' => 'Собствени',
+    'role_controlled_by_asset' => 'Controlled by the asset they are uploaded to',
+    'role_save' => 'Save Role',
+    'role_update_success' => 'Role successfully updated',
+    'role_users' => 'Users in this role',
+    'role_users_none' => 'No users are currently assigned to this role',
+
+    // Users
+    'users' => 'Users',
+    'user_profile' => 'User Profile',
+    'users_add_new' => 'Add New User',
+    'users_search' => 'Search Users',
+    'users_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 6 characters long.',
+    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
+    'users_send_invite_option' => 'Send user invite email',
+    'users_external_auth_id' => 'External Authentication ID',
+    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication 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',
+    'users_delete_warning' => 'This will fully delete this user with the name \':userName\' from the system.',
+    'users_delete_confirm' => 'Are you sure you want to delete this user?',
+    'users_delete_success' => 'Users successfully removed',
+    'users_edit' => 'Edit User',
+    'users_edit_profile' => 'Edit Profile',
+    'users_edit_success' => 'User successfully updated',
+    'users_avatar' => 'User Avatar',
+    'users_avatar_desc' => '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 revoke previously authorized access. Revoke access from your profile settings on the connected social account.',
+    'users_social_connect' => 'Connect Account',
+    'users_social_disconnect' => 'Disconnect Account',
+    'users_social_connected' => ':socialAccount account was successfully attached to your profile.',
+    'users_social_disconnected' => ':socialAccount account was successfully disconnected from your profile.',
+    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_create' => 'Create Token',
+    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_docs' => 'API Documentation',
+
+    // API Tokens
+    'user_api_token_create' => 'Create API Token',
+    'user_api_token_name' => 'Name',
+    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_success' => 'API token successfully created',
+    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token' => 'API Token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
+    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
+    'user_api_token_delete_success' => 'API token successfully deleted',
+
+    //! If editing translations files directly please ignore this in all
+    //! languages apart from en. Content will be auto-copied from en.
+    //!////////////////////////////////
+    'language_select' => [
+        'en' => 'English',
+        'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hu' => 'Magyar',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'nl' => 'Nederlands',
+        'pl' => 'Polski',
+        'pt_BR' => 'Português do Brasil',
+        'ru' => 'Русский',
+        'sk' => 'Slovensky',
+        'sl' => 'Slovenščina',
+        'sv' => 'Svenska',
+        'tr' => 'Türkçe',
+        'uk' => 'Українська',
+        'vi' => 'Tiếng Việt',
+        'zh_CN' => '简体中文',
+        'zh_TW' => '繁體中文',
+    ]
+    //!////////////////////////////////
+];
diff --git a/resources/lang/bg/validation.php b/resources/lang/bg/validation.php
new file mode 100644 (file)
index 0000000..76b57a2
--- /dev/null
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Validation Lines
+ * The following language lines contain the default error messages used by
+ * the validator class. Some of these rules have multiple versions such
+ * as the size rules. Feel free to tweak each of these messages here.
+ */
+return [
+
+    // Standard laravel validation lines
+    'accepted'             => 'The :attribute must be accepted.',
+    'active_url'           => 'The :attribute is not a valid URL.',
+    'after'                => 'The :attribute must be a date after :date.',
+    'alpha'                => 'The :attribute may only contain letters.',
+    'alpha_dash'           => 'The :attribute may only contain letters, numbers, dashes and underscores.',
+    'alpha_num'            => 'The :attribute may only contain letters and numbers.',
+    'array'                => 'The :attribute must be an array.',
+    'before'               => 'The :attribute must be a date before :date.',
+    'between'              => [
+        'numeric' => 'The :attribute must be between :min and :max.',
+        'file'    => 'The :attribute must be between :min and :max kilobytes.',
+        'string'  => 'The :attribute must be between :min and :max characters.',
+        'array'   => 'The :attribute must have between :min and :max items.',
+    ],
+    'boolean'              => 'The :attribute field must be true or false.',
+    'confirmed'            => 'The :attribute confirmation does not match.',
+    'date'                 => 'The :attribute is not a valid date.',
+    'date_format'          => 'The :attribute does not match the format :format.',
+    'different'            => 'The :attribute and :other must be different.',
+    'digits'               => 'The :attribute must be :digits digits.',
+    'digits_between'       => 'The :attribute must be between :min and :max digits.',
+    'email'                => 'The :attribute must be a valid email address.',
+    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'filled'               => 'The :attribute field is required.',
+    'gt'                   => [
+        'numeric' => 'The :attribute must be greater than :value.',
+        'file'    => 'The :attribute must be greater than :value kilobytes.',
+        'string'  => 'The :attribute must be greater than :value characters.',
+        'array'   => 'The :attribute must have more than :value items.',
+    ],
+    'gte'                  => [
+        'numeric' => 'The :attribute must be greater than or equal :value.',
+        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be greater than or equal :value characters.',
+        'array'   => 'The :attribute must have :value items or more.',
+    ],
+    'exists'               => 'The selected :attribute is invalid.',
+    'image'                => 'The :attribute must be an image.',
+    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'in'                   => 'The selected :attribute is invalid.',
+    'integer'              => 'The :attribute must be an integer.',
+    'ip'                   => 'The :attribute must be a valid IP address.',
+    'ipv4'                 => 'The :attribute must be a valid IPv4 address.',
+    'ipv6'                 => 'The :attribute must be a valid IPv6 address.',
+    'json'                 => 'The :attribute must be a valid JSON string.',
+    'lt'                   => [
+        'numeric' => 'The :attribute must be less than :value.',
+        'file'    => 'The :attribute must be less than :value kilobytes.',
+        'string'  => 'The :attribute must be less than :value characters.',
+        'array'   => 'The :attribute must have less than :value items.',
+    ],
+    'lte'                  => [
+        'numeric' => 'The :attribute must be less than or equal :value.',
+        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
+        'string'  => 'The :attribute must be less than or equal :value characters.',
+        'array'   => 'The :attribute must not have more than :value items.',
+    ],
+    'max'                  => [
+        'numeric' => 'The :attribute may not be greater than :max.',
+        'file'    => 'The :attribute may not be greater than :max kilobytes.',
+        'string'  => 'The :attribute may not be greater than :max characters.',
+        'array'   => 'The :attribute may not have more than :max items.',
+    ],
+    'mimes'                => 'The :attribute must be a file of type: :values.',
+    'min'                  => [
+        'numeric' => 'The :attribute must be at least :min.',
+        'file'    => 'The :attribute must be at least :min kilobytes.',
+        '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.',
+    'not_regex'            => 'The :attribute format is invalid.',
+    'numeric'              => 'The :attribute must be a number.',
+    'regex'                => 'The :attribute format is invalid.',
+    'required'             => 'The :attribute field is required.',
+    'required_if'          => 'The :attribute field is required when :other is :value.',
+    'required_with'        => 'The :attribute field is required when :values is present.',
+    'required_with_all'    => 'The :attribute field is required when :values is present.',
+    'required_without'     => 'The :attribute field is required when :values is not present.',
+    'required_without_all' => 'The :attribute field is required when none of :values are present.',
+    'same'                 => 'The :attribute and :other must match.',
+    'size'                 => [
+        'numeric' => 'The :attribute must be :size.',
+        'file'    => 'The :attribute must be :size kilobytes.',
+        'string'  => 'The :attribute must be :size characters.',
+        'array'   => 'The :attribute must contain :size items.',
+    ],
+    'string'               => 'The :attribute must be a string.',
+    'timezone'             => 'The :attribute must be a valid zone.',
+    'unique'               => 'The :attribute has already been taken.',
+    'url'                  => 'The :attribute format is invalid.',
+    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Password confirmation required',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 23fce810812f8d2c2b7e5002e82f95952f7ed6f1..92f4df8b67ce64fc44aa667b58a4376aad777b51 100644 (file)
@@ -6,43 +6,43 @@
 return [
 
     // Pages
-    'page_create'                 => 'vytvořená stránka',
+    'page_create'                 => 'vytvořil/a stránku',
     'page_create_notification'    => 'Stránka byla úspěšně vytvořena',
-    'page_update'                 => 'aktualizovaná stránka',
+    'page_update'                 => 'aktualizoval/a stránku',
     'page_update_notification'    => 'Stránka byla úspěšně aktualizována',
-    'page_delete'                 => 'smazaná stránka',
+    'page_delete'                 => 'odstranil/a stránku',
     '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',
+    'page_restore'                => 'obnovil/a stránku',
+    'page_restore_notification'   => 'Stránka byla úspěšně obnovena',
+    'page_move'                   => 'přesunul/a stránku',
 
     // Chapters
-    'chapter_create'              => 'vytvořená kapitola',
+    'chapter_create'              => 'vytvořil/a kapitolu',
     'chapter_create_notification' => 'Kapitola byla úspěšně vytvořena',
-    'chapter_update'              => 'aktualizovaná kapitola',
+    'chapter_update'              => 'aktualizoval/a kapitolu',
     'chapter_update_notification' => 'Kapitola byla úspěšně aktualizována',
-    'chapter_delete'              => 'smazaná kapitola',
+    'chapter_delete'              => 'smazal/a kapitolu',
     'chapter_delete_notification' => 'Kapitola byla úspěšně smazána',
-    'chapter_move'                => 'přesunutá kapitola',
+    'chapter_move'                => 'přesunul/a kapitolu',
 
     // Books
-    'book_create'                 => 'vytvořená kniha',
+    'book_create'                 => 'vytvořil/a knihu',
     'book_create_notification'    => 'Kniha byla úspěšně vytvořena',
-    'book_update'                 => 'aktualizovaná kniha',
+    'book_update'                 => 'aktualizoval/a knihu',
     'book_update_notification'    => 'Kniha byla úspěšně aktualizována',
-    'book_delete'                 => 'smazaná kniha',
+    'book_delete'                 => 'smazal/a knihu',
     'book_delete_notification'    => 'Kniha byla úspěšně smazána',
-    'book_sort'                   => 'seřazená kniha',
+    'book_sort'                   => 'seřadil/a knihu',
     '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_create'            => 'vytvořil/a knihovnu',
+    'bookshelf_create_notification'    => 'Knihovna byla úspěšně vytvořena',
+    'bookshelf_update'                 => 'aktualizoval/a knihovnu',
     'bookshelf_update_notification'    => 'Knihovna byla úspěšně aktualizována',
-    'bookshelf_delete'                 => 'smazaná knihovna',
-    'bookshelf_delete_notification'    => 'Knihovna byla úspěšně smazána',
+    'bookshelf_delete'                 => 'odstranil/a knihovnu',
+    'bookshelf_delete_notification'    => 'Knihovna byla úspěšně odstraněna',
 
     // Other
-    'commented_on'                => 'okomentováno v',
+    'commented_on'                => 'okomentoval/a',
 ];
index 0e841686d5d52eaf5755099fa05b5e8d67ddb6a8..c59ee85d90e70a77a9fc424c74443ecec4bd902e 100644 (file)
@@ -6,72 +6,72 @@
  */
 return [
 
-    'failed' => 'Neplatné přihlašovací údaje.',
-    'throttle' => 'Příliš pokusů o přihlášení. Zkuste to prosím znovu za :seconds sekund.',
+    'failed' => 'Tyto přihlašovací údaje neodpovídají našim záznamům.',
+    'throttle' => 'Příliš mnoho 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',
+    'log_in_with' => 'Přihlásit se pomocí :socialDriver',
+    'sign_up_with' => 'Registrovat se pomocí :socialDriver',
     'logout' => 'Odhlásit',
 
     'name' => 'Jméno',
-    'username' => 'Jméno účtu',
+    'username' => 'Uživatelské jméno',
     'email' => 'E-mail',
     'password' => 'Heslo',
-    'password_confirm' => 'Potvrdit heslo',
-    'password_hint' => 'Musí mít víc než 7 znaků',
+    'password_confirm' => 'Oveření hesla',
+    'password_hint' => 'Musí mít více než 7 znaků',
     'forgot_password' => 'Zapomněli jste heslo?',
-    'remember_me' => 'Neodhlašovat',
+    'remember_me' => 'Zapamatovat si mě',
     'ldap_email_hint' => 'Zadejte email, který chcete přiřadit k tomuto účtu.',
     'create_account' => 'Vytvořit účet',
-    'already_have_account' => 'Máte už založený účet?',
-    'dont_have_account' => 'Nemáte uč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',
+    'already_have_account' => 'Již máte účet?',
+    'dont_have_account' => 'Nemáte účet?',
+    'social_login' => 'Přihlášení pomocí sociálních sítí',
+    'social_registration' => 'Přihlášení pomocí sociálních sítí',
+    'social_registration_text' => 'Registrovat a přihlásit se pomocí jiné služby.',
 
-    '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í.',
+    'register_thanks' => 'Děkujeme za registraci!',
+    'register_confirm' => 'Zkontrolujte prosím svůj e-mail a klikněte na potvrzovací tlačítko pro přístup do :appName.',
+    'registrations_disabled' => 'Registrace jsou aktuálně zakázány',
+    'registration_email_domain_invalid' => 'Tato e-mailová doména nemá přístup k této aplikaci',
+    'register_success' => 'Děkujeme za registraci! Nyní jste zaregistrováni a přihlášeni.',
 
 
     // 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' => 'A password reset link will be sent to :email if that email address is found in the system.',
-    '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.',
+    'reset_password' => 'Obnovit heslo',
+    'reset_password_send_instructions' => 'Níže zadejte svou e-mailovou adresu a bude vám zaslán e-mail s odkazem pro obnovení hesla.',
+    'reset_password_send_button' => 'Zaslat odkaz pro obnovení',
+    'reset_password_sent' => 'Odkaz pro obnovení hesla bude odeslán na :email, pokud bude tato e-mailová adresa nalezena v systému.',
+    'reset_password_success' => 'Vaše heslo bylo úspěšně obnoveno.',
+    'email_reset_subject' => 'Obnovit heslo do :appName',
+    'email_reset_text' => 'Tento e-mail jste obdrželi, protože jsme obdrželi žádost o obnovení hesla k vašemu účtu.',
+    'email_reset_not_requested' => 'Pokud jste o obnovení hesla nežádali, není vyžadována žádná další akce.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => 'Potvrďte vaši emailovou adresu pro :appName',
+    'email_confirm_subject' => 'Potvrďte svůj e-mail 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_confirm_text' => 'Prosíme potvrďte svou e-mailovou adresu kliknutím na níže uvedené tlačítko:',
+    'email_confirm_action' => 'Potvrdit e-mail',
+    'email_confirm_send_error' => 'Potvrzení e-mailu je vyžadováno, ale systém nemohl odeslat e-mail. Obraťte se na správce, abyste se ujistili, že je e-mail správně nastaven.',
+    'email_confirm_success' => 'Váš e-mail byla potvrzen!',
+    'email_confirm_resent' => 'E-mail s potvrzením byl znovu odeslán. Zkontrolujte svou příchozí poštu.',
 
-    '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',
+    'email_not_confirmed' => 'E-mailová adresa nebyla potvrzena',
+    'email_not_confirmed_text' => 'Vaše e-mailová adresa nebyla dosud potvrzena.',
+    'email_not_confirmed_click_link' => 'Klikněte prosím na odkaz v e-mailu, který byl odeslán krátce po registraci.',
+    'email_not_confirmed_resend' => 'Pokud nemůžete e-mail nalézt, můžete znovu odeslat potvrzovací e-mail odesláním níže uvedeného formuláře.',
+    'email_not_confirmed_resend_button' => 'Znovu odeslat potvrzovací e-mail',
 
     // User Invite
-    'user_invite_email_subject' => 'Byl jste pozván do :appName!',
+    'user_invite_email_subject' => 'Byli jste pozváni přidat se do :appName!',
     'user_invite_email_greeting' => 'Byl pro vás vytvořen účet na :appName.',
-    'user_invite_email_text' => 'Klikněte na tlačítko níže pro nastavení hesla k účtu a získání přístupu:',
-    'user_invite_email_action' => 'Nastavit heslo účtu',
+    'user_invite_email_text' => 'Klikněte na níže uvedené tlačítko pro nastavení hesla k účtu a získání přístupu:',
+    'user_invite_email_action' => 'Nastavit heslo účtu',
     'user_invite_page_welcome' => 'Vítejte v :appName!',
-    'user_invite_page_text' => 'Chcete-li dokončit svůj účet a získat přístup, musíte nastavit heslo, které bude použito k přihlášení do :appName při budoucích návštěvách.',
+    'user_invite_page_text' => 'Pro dokončení vašeho účtu a získání přístupu musíte nastavit heslo, které bude použito k přihlášení do :appName při budoucích návštěvách.',
     'user_invite_page_confirm_button' => 'Potvrdit heslo',
     'user_invite_success' => 'Heslo nastaveno, nyní máte přístup k :appName!'
-];
\ No newline at end of file
+];
index 3586a61260c649518139d3801d09148b87046875..bdfaa2eea3d940fd879015adf675877cea2fd716 100644 (file)
@@ -5,38 +5,39 @@
 return [
 
     // Buttons
-    'cancel' => 'Storno',
+    'cancel' => 'Zrušit',
     'confirm' => 'Potvrdit',
     'back' => 'Zpět',
     'save' => 'Uložit',
     'continue' => 'Pokračovat',
-    'select' => 'Zvolit',
+    'select' => 'Vybrat',
     'toggle_all' => 'Přepnout vše',
     'more' => 'Více',
 
     // Form Labels
-    'name' => 'Jméno',
+    'name' => 'Název',
     'description' => 'Popis',
-    'role' => 'Funkce',
-    'cover_image' => 'Obrázek na přebal',
-    'cover_image_description' => 'Obrázek by měl být asi 440 × 250px.',
+    'role' => 'Role',
+    'cover_image' => 'Obrázek obálky',
+    'cover_image_description' => 'Obrázek by měl být přibližně 440×250px.',
     
     // Actions
     'actions' => 'Akce',
-    'view' => 'Pohled',
+    'view' => 'Zobrazit',
     'view_all' => 'Zobrazit vše',
     'create' => 'Vytvořit',
     'update' => 'Aktualizovat',
     'edit' => 'Upravit',
-    'sort' => 'Řadit',
+    'sort' => 'Seřadit',
     'move' => 'Přesunout',
     'copy' => 'Kopírovat',
     'reply' => 'Odpovědět',
-    'delete' => 'Smazat',
+    'delete' => 'Odstranit',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Hledat',
-    'search_clear' => 'Vyčistit hledání',
-    'reset' => 'Resetovat',
-    'remove' => 'Odstranit',
+    'search_clear' => 'Vymazat hledání',
+    'reset' => 'Obnovit',
+    'remove' => 'Odebrat',
     'add' => 'Přidat',
     'fullscreen' => 'Celá obrazovka',
 
@@ -45,35 +46,35 @@ return [
     'sort_direction_toggle' => 'Přepínač směru řazení',
     'sort_ascending' => 'Řadit vzestupně',
     'sort_descending' => 'Řadit sestupně',
-    'sort_name' => 'Jméno',
+    'sort_name' => 'Název',
     'sort_created_at' => 'Datum vytvoření',
     'sort_updated_at' => 'Datum aktualizace',
 
     // Misc
-    'deleted_user' => 'Smazaný uživatel',
+    'deleted_user' => 'Odstraněný uživatel',
     'no_activity' => 'Žádná aktivita k zobrazení',
-    'no_items' => 'Žádné položky nejsou k mání',
+    'no_items' => 'Žádné položky k dispozici',
     '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',
+    'toggle_details' => 'Přepnout podrobnosti',
+    'toggle_thumbnails' => 'Přepnout náhledy',
+    'details' => 'Podrobnosti',
+    'grid_view' => 'Zobrazení mřížky',
+    'list_view' => 'Zobrazení seznamu',
     'default' => 'Výchozí',
     'breadcrumb' => 'Drobečková navigace',
 
     // Header
     'profile_menu' => 'Nabídka profilu',
-    'view_profile' => 'Ukázat profil',
+    'view_profile' => 'Zobrazit profil',
     'edit_profile' => 'Upravit profil',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Tmavý režim',
+    'light_mode' => 'Světelný režim',
 
     // Layout tabs
-    'tab_info' => 'Info',
+    'tab_info' => 'Informace',
     'tab_content' => 'Obsah',
 
     // 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_action_help' => 'Pokud se vám nedaří kliknout na tlačítko „:actionText“, zkopírujte a vložte níže uvedenou URL do vašeho webového prohlížeče:',
     'email_rights' => 'Všechna práva vyhrazena',
 ];
index a62914f1bc680c21ba9077eae4444dd6f7c9c540..178f92149489058a476e9d3bc38e2eff61c7c1ff 100644 (file)
@@ -5,29 +5,30 @@
 return [
 
     // Image Manager
-    'image_select' => 'Volba obrázku',
+    'image_select' => 'Výběr 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_book_title' => 'Zobrazit obrázky nahrané do této knihy',
+    'image_page_title' => 'Zobrazit obrázky nahrané na tuto stránku',
     '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_delete_used' => 'Tento obrázek je použit na níže uvedených stránkách.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_select_image' => 'Vyberte obrázek',
+    'image_dropzone' => 'Přetáhněte obrázky nebo klikněte sem pro nahrání',
+    'images_deleted' => 'Obrázky odstraně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',
+    'image_delete_success' => 'Obrázek byl úspěšně odstraněn',
+    'image_upload_remove' => 'Odebrat',
 
     // Code Editor
     'code_editor' => 'Upravit kód',
     'code_language' => 'Jazyk kódu',
     'code_content' => 'Obsah kódu',
+    'code_session_history' => 'Historie relace',
     'code_save' => 'Uložit kód',
 ];
index 579d49127b86f2a312f796aa806775b38c184920..f8597ebb5961a43118c43f86c8c1c0c7466a2d9b 100644 (file)
@@ -11,72 +11,73 @@ return [
     '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_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'Nedávno vytvořené knihovny',
     'recently_update' => 'Nedávno aktualizované',
-    'recently_viewed' => 'Nedávno prohlížené',
-    'recent_activity' => 'Nedávné činnosti',
-    'create_now' => 'Vytvořte jí',
+    'recently_viewed' => 'Nedávno zobrazené',
+    'recent_activity' => 'Nedávné aktivity',
+    'create_now' => 'Vytvořte ji nyní',
     'revisions' => 'Revize',
-    'meta_revision' => 'Revize #:revisionCount',
+    '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',
+    'entity_select' => 'Výběr entity',
     '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)',
+    'my_recently_viewed' => 'Mé nedávno zobrazené',
+    'no_pages_viewed' => 'Nezobrazili jste žádné stránky',
+    'no_pages_recently_created' => 'Nedávno nebyly vytvořeny žádné stránky',
+    'no_pages_recently_updated' => 'Nedávno nebyly aktualizovány žádné stránky',
+    'export' => 'Exportovat',
+    'export_html' => 'Konsolidovaný webový soubor',
+    'export_pdf' => 'Soubor PDF',
+    'export_text' => 'Textový soubor',
 
     // 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',
+    'permissions' => 'Oprávnění',
+    'permissions_intro' => 'Pokud je povoleno, tato oprávnění budou mít přednost před všemi nastavenými oprávněními role.',
+    'permissions_enable' => 'Povolit vlastní oprávnění',
+    'permissions_save' => 'Uložit oprávnění',
 
     // 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_clear' => 'Vymazat hledání',
+    'search_no_pages' => 'Tomuto hledání neodpovídají žádné stránky',
     'search_for_term' => 'Hledat :term',
     'search_more' => 'Další výsledky',
-    'search_filters' => 'Filtry hledání',
+    'search_advanced' => 'Rozšířené hledání',
+    'search_terms' => 'Hledané výrazy',
     '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_exact_matches' => 'Přesné shody',
+    'search_tags' => 'Hledat štítky',
+    'search_options' => 'Možnosti',
+    'search_viewed_by_me' => 'Zobrazeno mnou',
+    'search_not_viewed_by_me' => 'Nezobrazeno mnou',
+    'search_permissions_set' => 'Sada oprávnění',
     'search_created_by_me' => 'Vytvořeno mnou',
-    'search_updated_by_me' => 'Aktualizováno',
-    'search_date_options' => 'Volby datumu',
+    'search_updated_by_me' => 'Aktualizováno mnou',
+    'search_date_options' => 'Možnosti data',
     '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',
+    'search_set_date' => 'Nastavit datum',
+    'search_update' => 'Aktualizovat hledání',
 
     // Shelves
     'shelf' => 'Knihovna',
     'shelves' => 'Knihovny',
-    'x_shelves' => ':count Shelf|:count Shelves',
+    'x_shelves' => '{0}:count knihoven|{1}:count knihovna|[2,4]:count knihovny|[5,*]:count knihoven',
     'shelves_long' => 'Knihovny',
-    'shelves_empty' => 'Žádné knihovny nebyly vytvořeny',
+    'shelves_empty' => 'Nebyly vytvořeny žádné knihovny',
     'shelves_create' => 'Vytvořit novou knihovnu',
     'shelves_popular' => 'Populární knihovny',
     'shelves_new' => 'Nové knihovny',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => 'Nová Knihovna',
     'shelves_popular_empty' => 'Nejpopulárnější knihovny se objeví zde.',
-    'shelves_new_empty' => 'Nejnovější knihovny se objeví zde.',
+    'shelves_new_empty' => 'Zde se objeví nejnověji vytvořené knihovny.',
     'shelves_save' => 'Uložit knihovnu',
     'shelves_books' => 'Knihy v této knihovně',
     'shelves_add_books' => 'Přidat knihy do knihovny',
@@ -85,67 +86,67 @@ return [
     '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)',
+    'shelves_delete' => 'Odstranit knihovnu',
+    'shelves_delete_named' => 'Odstranit knihovnu :name',
+    'shelves_delete_explain' => "Toto odstraní knihovnu s názvem ‚:name‘. Obsažené knihy nebudou odstraněny.",
+    'shelves_delete_confirmation' => 'Opravdu chcete odstranit tuto knihovnu?',
+    'shelves_permissions' => 'Oprávnění knihovny',
+    'shelves_permissions_updated' => 'Oprávnění knihovny byla aktualizována',
+    'shelves_permissions_active' => 'Oprávnění knihovny jsou aktivní',
+    'shelves_copy_permissions_to_books' => 'Kopírovat oprávnění na knihy',
+    'shelves_copy_permissions' => 'Kopírovat oprávnění',
+    'shelves_copy_permissions_explain' => 'Toto použije aktuální nastavení oprávnění této knihovny na všechny knihy v ní obsažené. Před aktivací se ujistěte, že byly uloženy všechny změny oprávnění této knihovny.',
+    'shelves_copy_permission_success' => 'Oprávnění knihovny byla zkopírována na :count knih',
 
     // 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',
+    'x_books' => '{0}:count knih|{1}:count kniha|[2,4]:count knihy|[5,*]:count knih',
+    'books_empty' => 'Nebyly vytvořeny žádné knihy',
+    'books_popular' => 'Oblíbené knihy',
     'books_recent' => 'Nedávné knihy',
     'books_new' => 'Nové knihy',
-    'books_new_action' => 'New Book',
-    'books_popular_empty' => 'Zde budou zobrazeny nejpopulárnější knihy.',
-    'books_new_empty' => 'Zde budou zobrazeny nově vytvořené knihy.',
+    'books_new_action' => 'Nová kniha',
+    'books_popular_empty' => 'Zde se objeví nejoblíbenější knihy.',
+    'books_new_empty' => 'Zde se objeví nejnověji 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_delete' => 'Odstranit knihu',
+    'books_delete_named' => 'Odstranit knihu :bookName',
+    'books_delete_explain' => 'Toto odstraní knihu s názvem ‚:bookName‘. Všechny stránky a kapitoly budou odebrány.',
+    'books_delete_confirmation' => 'Opravdu chcete odstranit tuto knihu?',
     '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_permissions' => 'Oprávnění knihy',
+    'books_permissions_updated' => 'Oprávnění knihy aktualizována',
+    'books_empty_contents' => 'Pro tuto knihu nebyly vytvořeny žádné stránky nebo kapitoly.',
     'books_empty_create_page' => 'Vytvořit novou stránku',
-    'books_empty_sort_current_book' => 'Seřadit tuto knihu',
+    'books_empty_sort_current_book' => 'Seřadit aktuální knihu',
     'books_empty_add_chapter' => 'Přidat kapitolu',
-    'books_permissions_active' => 'Účinná práva knihy',
+    'books_permissions_active' => 'Oprávnění knihy jsou aktivní',
     'books_search_this' => 'Prohledat tuto knihu',
-    'books_navigation' => 'Obsah knihy',
+    'books_navigation' => 'Navigace knihy',
     'books_sort' => 'Seřadit obsah knihy',
     'books_sort_named' => 'Seřadit knihu :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' => 'Ukázat ostatní knihy',
+    'books_sort_name' => 'Seřadit podle názvu',
+    'books_sort_created' => 'Seřadit podle data vytvoření',
+    'books_sort_updated' => 'Seřadit podle data aktualizace',
+    'books_sort_chapters_first' => 'Kapitoly první',
+    'books_sort_chapters_last' => 'Kapitoly poslední',
+    'books_sort_show_other' => 'Zobrazit 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',
+    'x_chapters' => '{0}:count Kapitol|{1}:count Kapitola|[2,4]:count Kapitoly|[5,*]: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_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',
@@ -162,7 +163,7 @@ return [
     // Pages
     'page' => 'Stránka',
     'pages' => 'Stránky',
-    'x_pages' => ':count strana|:count strany|:count strany|:count strany|:count stran',
+    'x_pages' => '{0}:count Stran|{1}:count Strana|[2,4]:count Strany|[5,*]:count Stran',
     'pages_popular' => 'Populární stránky',
     'pages_new' => 'Nová stránka',
     'pages_attachments' => 'Přílohy',
@@ -170,55 +171,55 @@ return [
     '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_delete_draft' => 'Odstranit koncept stránky',
+    'pages_delete_success' => 'Stránka odstraněna',
+    'pages_delete_draft_success' => 'Koncept stránky odstraněn',
+    'pages_delete_confirm' => 'Opravdu chcete odstranit tuto stránku?',
+    'pages_delete_draft_confirm' => 'Opravdu chcete odstranit tento koncept stránky?',
     'pages_editing_named' => 'Úpravy stránky :pageName',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Možnosti konceptu',
     'pages_edit_save_draft' => 'Uložit koncept',
     'pages_edit_draft' => 'Upravit koncept stránky',
-    'pages_editing_draft' => 'Úpravy konceptu',
+    'pages_editing_draft' => 'Úprava konceptu',
     'pages_editing_page' => 'Úpravy stránky',
     'pages_edit_draft_save_at' => 'Koncept uložen v ',
-    'pages_edit_delete_draft' => 'Smazat koncept',
+    'pages_edit_delete_draft' => 'Odstranit 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_edit_set_changelog' => 'Nastavit protokol změn',
+    'pages_edit_enter_changelog_desc' => 'Zadejte stručný popis změn, které jste provedli',
+    'pages_edit_enter_changelog' => 'Zadejte protokol změn',
     '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_link' => 'Vložit odkaz na entitu',
     'pages_md_insert_drawing' => 'Vložit kresbu',
-    'pages_not_in_chapter' => 'Stránka není součástí žádné kapitoly',
+    'pages_not_in_chapter' => 'Stránka není v kapitole',
     '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_permissions' => 'Oprávnění stránky',
+    'pages_permissions_success' => 'Oprávnění 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_named' => 'Revize stránky pro :pageName',
+    'pages_revision_named' => 'Revize stránky pro :pageName',
     'pages_revisions_created_by' => 'Vytvořeno uživatelem',
     'pages_revisions_date' => 'Datum revize',
-    'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => 'Komentáře změn',
+    'pages_revisions_number' => 'Č.',
+    'pages_revisions_numbered' => 'Revize č. :id',
+    'pages_revisions_numbered_changes' => 'Změny revize č. :id',
+    'pages_revisions_changelog' => 'Protokol změn',
     'pages_revisions_changes' => 'Změny',
     'pages_revisions_current' => 'Aktuální verze',
     'pages_revisions_preview' => 'Náhled',
-    'pages_revisions_restore' => 'Renovovat',
+    'pages_revisions_restore' => 'Obnovit',
     'pages_revisions_none' => 'Tato stránka nemá žádné revize',
-    'pages_copy_link' => 'Zkopírovat odkaz',
+    'pages_copy_link' => 'Kopírovat odkaz',
     'pages_edit_content_link' => 'Upravit obsah',
     'pages_permissions_active' => 'Účinná práva stránky',
     'pages_initial_revision' => 'První vydání',
@@ -227,14 +228,14 @@ return [
     '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',
+        'start_b' => ':userName začal/a 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_draft_discarded' => 'Koncept byl zahozen. Editor nyní obsahuje aktuální verzi stránky.',
     'pages_specific' => 'Konkrétní stránka',
-    'pages_is_template' => 'Page Template',
+    'pages_is_template' => 'Šablona stránky',
 
     // Editor Sidebar
     'page_tags' => 'Štítky stránky',
@@ -243,11 +244,11 @@ return [
     'shelf_tags' => 'Štítky knihovny',
     'tag' => 'Štítek',
     'tags' =>  'Štítky',
-    'tag_name' =>  'Tag Name',
-    'tag_value' => 'Hodnota Å títku (volitelné)',
+    'tag_name' =>  'Název štítku',
+    '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',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Odstranit tento š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í.',
@@ -255,15 +256,16 @@ return [
     '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_delete' => 'Are you sure you want to delete this attachment?',
     '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_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_insert_link' => 'Add Attachment Link to Page',
     '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.',
@@ -273,26 +275,26 @@ return [
     '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',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Šablony',
+    'templates_set_as_template' => 'Tato stránka je šablona',
+    'templates_explain_set_as_template' => 'Tuto stránku můžete nastavit jako šablonu, aby byl její obsah využit při vytváření dalších stránek. Ostatní uživatelé budou moci použít tuto šablonu, pokud mají oprávnění k zobrazení této stránky.',
+    'templates_replace_content' => 'Nahradit obsah stránky',
+    'templates_append_content' => 'Připojit za obsah stránky',
+    'templates_prepend_content' => 'Připojit před obsah stránky',
 
     // Profile View
     'profile_user_for_x' => 'Uživatelem již :time',
     'profile_created_content' => 'Vytvořený obsah',
-    'profile_not_created_pages' => ':userName nevytvoÅ\99il/a Å¾Ã¡dný obsah',
+    'profile_not_created_pages' => ':userName nevytvoÅ\99il/a Å¾Ã¡dné stránky',
     'profile_not_created_chapters' => ':userName nevytvořil/a žádné kapitoly',
     'profile_not_created_books' => ':userName nevytvořil/a žádné knihy',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName nevytvořil/a žádné knihovny',
 
     // Comments
     'comment' => 'Komentář',
     'comments' => 'Komentáře',
     'comment_add' => 'Přidat komentář',
-    'comment_placeholder' => 'Zanechat komentář zde',
+    'comment_placeholder' => 'Zanechte 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...',
@@ -308,7 +310,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Opravdu chcete smazat tuto revizi?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Jste si jisti, že chcete obnovit tuto revizi? Aktuální obsah stránky bude nahrazen.',
     'revision_delete_success' => 'Revize smazána',
     'revision_cannot_delete_latest' => 'Nelze smazat poslední revizi.'
-];
\ No newline at end of file
+];
index f3b87fc02f178db5eaf272570e80f99e3987eb1b..a498906cb90b450de1ccad39373052ff0ea59dfb 100644 (file)
@@ -9,31 +9,31 @@ return [
     '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.',
+    'error_user_exists_different_creds' => 'Uživatel s e-mailem :email již existuje ale s jinými přihlašovacími údaji.',
+    'email_already_confirmed' => 'E-mailová 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.',
-    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'email_confirmation_awaiting' => 'E-mailová adresa pro používaný účet musí být potvrzena',
     '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.',
-    'saml_already_logged_in' => 'Already logged in',
-    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_already_logged_in' => 'Již jste přihlášeni',
+    'saml_user_not_registered' => 'Uživatel :name není registrován a automatická registrace je zakázána',
     'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
     'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
-    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'saml_fail_authed' => 'Přihlášení pomocí :system selhalo, systém neposkytl úspěšnou autorizaci',
     '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_email_in_use' => 'E-mailová 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.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'invite_token_expired' => 'Odkaz v pozvánce již bohužel expiroval. Namísto toho ale můžete zkusit resetovat heslo do Vašeho účtu.',
 
     // System
     'path_not_writable' => 'Nelze zapisovat na cestu k souboru :filePath. Zajistěte aby se dalo nahrávat na server.',
@@ -46,7 +46,6 @@ return [
     '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
@@ -61,11 +60,11 @@ return [
     '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.',
+    'guests_cannot_save_drafts' => 'Nepřihlášení návštěvníci 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',
+    'users_cannot_delete_guest' => 'Uživatele Guest není možno smazat',
 
     // Roles
     'role_cannot_be_edited' => 'Tuto roli nelze editovat',
@@ -74,30 +73,30 @@ return [
     '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.',
+    'comment_list' => 'Při načítá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_add' => 'Při přidávání / úpravě komentáře nastala chyba.',
     'comment_delete' => 'Při mazání komentáře nastala chyba.',
     'empty_comment' => 'Nemůžete přidat prázdný komentář.',
 
     // Error pages
     '404_page_not_found' => 'Stránka nenalezena',
-    'sorry_page_not_found' => 'Omlouváme se, ale stránka, kterou hledáte nebyla nalezena.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found' => 'Omlouváme se, ale stránka, kterou hledáte, nebyla nalezena.',
+    'sorry_page_not_found_permission_warning' => 'Pokud myslíte, že by stránka měla existovat, možná jen nemáte oprávnění pro její zobrazení.',
     'return_home' => 'Návrat domů',
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálně vypnutá',
     'back_soon' => 'Brzy naběhne.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
-    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
-    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
-    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
-    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_no_authorization_found' => 'V požadavku nebyla nalezen žádný autorizační token',
+    'api_bad_authorization_format' => 'V požadavku byl nalezen autorizační token, ale jeho formát se zdá být chybný',
+    'api_user_token_not_found' => 'Pro poskytnutý autorizační token nebyl nalezen žádný odpovídající API token',
+    'api_incorrect_token_secret' => 'Poskytnutý Token Secret neodpovídá použitému API tokenu',
+    'api_user_no_api_permission' => 'Vlastník použitého API tokenu nemá oprávnění provádět API volání',
+    'api_user_token_expired' => 'Platnost autorizačního tokenu vypršela',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => 'Při posílání testovacího e-mailu nastala chyba:',
 
 ];
index 6281ff05845c0668cbbd09b92900b2220c14f59e..eb0beabb9898819cc73954014fc5e27d3912f570 100644 (file)
@@ -6,7 +6,7 @@
  */
 return [
 
-    'previous' => '\'&laquo; P',
-    'next'     => 'Dal',
+    'previous' => '&laquo; Předchozí',
+    'next'     => 'Další &raquo;',
 
 ];
index 368523630cf6213317324e8f082be82dc6f9b8a1..cc5669d4825d6e4892c20e9b149d2879c6bdcbdd 100644 (file)
@@ -6,10 +6,10 @@
  */
 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' => 'The password reset token is invalid for this email address.',
-    'sent' => 'Poslali jsme vám odkaz pro reset hesla!',
-    'reset' => 'Vaše heslo bylo resetováno!',
+    'password' => 'Heslo musí mít alespoň osm znaků a musí odpovídat potvrzení.',
+    'user' => "Nemůžeme nalézt uživatele s touto e-mailovou adresou.",
+    'token' => 'Token pro obnovení hesla je neplatný pro tuto e-mailovou adresu.',
+    'sent' => 'Poslali jsme vám e-mail s odkazem pro obnovení hesla!',
+    'reset' => 'Vaše heslo bylo obnoveno!',
 
 ];
index a5b60ba583bdc8a2fa9bffd98e4300286dd376a0..5f7e1a470c0bc1a7937857e04141be3b3b294e89 100644 (file)
@@ -12,84 +12,98 @@ return [
     'settings_save_success' => 'Nastavení bylo uloženo',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'Přizpůsobení',
+    'app_features_security' => 'Funkce a zabezpečení',
     '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_desc' => 'Název se bude zobrazovat v záhlaví této aplikace a v odesílaných e-mailech.',
     'app_name_header' => 'Zobrazovát název aplikace v záhlaví?',
-    '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_access' => 'Veřejný přístup',
+    'app_public_access_desc' => 'Povolení této volby umožní návštěvníkům, kteří nejsou přihlášeni, přístup k obsahu v instanci BookStack.',
+    'app_public_access_desc_guest' => 'Přístup veřejnosti je možné kontrolovat prostřednictvím uživatele "Guest".',
+    'app_public_access_toggle' => 'Povolit veřejný přístup',
     'app_public_viewing' => 'Povolit prohlížení veřejností?',
-    'app_secure_images' => 'Nahrávat obrázky neveřejně a zabezpečeně?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
-    'app_secure_images_desc' => 'Z výkonnostních důvodů jsou vÅ¡echny obrázky veÅ\99ejné. Tato volba pÅ\99idá do adresy obrázku náhodné Ä\8díslo, aby nikdo neodhadnul adresu obrázku. ZajistÄ\9bte aÅ¥ adresáÅ\99e nikomu nezobrazují seznam souborů.',
+    'app_secure_images' => 'Povolit vyšší zabezpečení obrázků ?',
+    'app_secure_images_toggle' => 'Povolit vyšší zabezpečení obrázků',
+    'app_secure_images_desc' => 'Z výkonnostních důvodů jsou vÅ¡echny obrázky veÅ\99ejné. Tato volba pÅ\99idá do adresy obrázku náhodný Å\99etÄ\9bzec, aby nikdo neodhadnul adresu obrázku. UjistÄ\9bte se, Å¾e není povoleno indexování adresáÅ\99ů, abyste zamezili snadnému pÅ\99ístupu.',
     '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_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
+    'app_custom_html_disabled_notice' => 'Vlastní HTML hlavička je na této stránce nastavení zakázána, aby bylo možné vrátit změny zpět.',
     '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_desc' => 'Zvolte pohled, který se použije jako úvodní stránka. U zvolených stránek bude ignorováno jejich oprávnění.',
     'app_homepage_select' => 'Zvolte stránku',
     'app_disable_comments' => 'Zakázání komentářů',
-    'app_disable_comments_toggle' => 'Disable comments',
-    'app_disable_comments_desc' => 'Zakáže komentáře napříč všemi stránkami. Existující komentáře se přestanou zobrazovat.',
+    'app_disable_comments_toggle' => 'Zakázat komentáře',
+    'app_disable_comments_desc' => 'Zakáže komentáře napříč všemi stránkami. <br> Existující komentáře se přestanou zobrazovat.',
 
     // Color settings
-    'content_colors' => 'Content Colors',
-    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
-    'bookshelf_color' => 'Shelf Color',
-    'book_color' => 'Book Color',
-    'chapter_color' => 'Chapter Color',
-    'page_color' => 'Page Color',
+    'content_colors' => 'Barvy obsahu',
+    'content_colors_desc' => 'Nastaví barvy pro všechny prvky v hierarchii organizace stránek. Pro čitelnost je doporučeno zvolit barvy s podobným jasem jako výchozí barvy.',
+    'bookshelf_color' => 'Barva Knihovny',
+    'book_color' => 'Barva Knihy',
+    'chapter_color' => 'Barva Kapitoly',
+    'page_color' => 'Barva Stránky',
     'page_draft_color' => 'Page Draft Color',
 
     // Registration Settings
     'reg_settings' => 'Nastavení registrace',
-    '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_enable' => 'Povolit Registrace',
+    'reg_enable_toggle' => 'Povolit registrace',
+    'reg_enable_desc' => 'Při povolení registrace se budou moct tito uživatelé přihlásit a obdrží výchozí uživatelskou roli.',
     'reg_default_role' => 'Role přiřazená po registraci',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
+    'reg_enable_external_warning' => 'Výše uvedená volba je ignorována, pokud je aktivní externí LDAP nebo SAML ověření. Uživatelské účty pro neexistující členy budou automaticky vytvořeny po přihlášení přes externí autentifikační systém.',
+    'reg_email_confirmation' => 'Potvrzení e-mailem',
+    'reg_email_confirmation_toggle' => 'Vyžadovat potvrzení e-mailem',
     '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',
+    'reg_confirm_restrict_domain_placeholder' => 'Žádná omezení nebyla nastavena',
 
     // 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' => 'Promazá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_run' => 'Spustit Promazá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.',
-    'maint_send_test_email' => 'Send a Test Email',
-    'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
-    'maint_send_test_email_run' => 'Send test email',
-    'maint_send_test_email_success' => 'Email sent to :address',
+    'maint_send_test_email' => 'Odeslat zkušební e-mail',
+    'maint_send_test_email_desc' => 'Toto pošle zkušební e-mail na vaši e-mailovou adresu uvedenou ve vašem profilu.',
+    'maint_send_test_email_run' => 'Odeslat zkušební e-mail',
+    'maint_send_test_email_success' => 'E-mail odeslán na :address',
     'maint_send_test_email_mail_subject' => 'Test Email',
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // 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_confirm' => "Role ':roleName' bude smazána.",
+    'role_delete_users_assigned' => 'Role je přiřazena :userCount uživatelům. Pokud je chcete přesunout do jiné role, zvolte jednu z následujících.',
+    'role_delete_no_migration' => 'Nepřiřazovat uživatelům novou roli',
     'role_delete_sure' => 'Opravdu chcete tuto roli smazat?',
     'role_delete_success' => 'Role byla úspěšně smazána',
     'role_edit' => 'Upravit roli',
@@ -102,82 +116,83 @@ return [
     '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_page_templates' => 'Manage page templates',
-    'role_access_api' => 'Access system API',
+    'role_manage_page_templates' => 'Spravovat šablony stránek',
+    'role_access_api' => 'Přístup k API systému',
     'role_manage_settings' => 'Správa nastavení aplikace',
     'role_asset' => 'Práva děl',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     '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_controlled_by_asset' => 'Řídí se obsahem 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.',
+    'role_update_success' => 'Role úspěšně upravena',
+    'role_users' => 'Uživatelé, kteří mají tuto roli',
+    'role_users_none' => 'Žádný uživatel tuto roli nemá.',
 
     // 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_details' => 'User Details',
+    'users_details' => 'Údaje o uživateli',
     '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' => 'Uživatelské role',
-    '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_role_desc' => 'Vyberte, do kterých rolí bude uživatel přiřazen. Pokud je uživatel přiřazen k více rolím, oprávnění z těchto rolí se budou skládat a budou dostávat všechny schopnosti přiřazených rolí.',
+    'users_password' => 'Uživatelské heslo',
     'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
     'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
     'users_send_invite_option' => 'Send user invite email',
     'users_external_auth_id' => 'Přihlašovací identifikátory třetích stran',
     'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
     '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_system_public' => 'Symbolizuje každého nepřihlášené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 nepřihlášeným.',
     'users_delete' => 'Smazat uživatele',
-    'users_delete_named' => 'Smazat uživatele :userName',
+    'users_delete_named' => 'Odstranit 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' => 'Obrázek uživatele',
     '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_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_preferred_language' => 'Preferovaný jazyk',
+    'users_preferred_language_desc' => 'tato volba ovlivní pouze jazyk používaný v uživatelském rozhraní aplikace. Vobla nemá vliv na žádný uživateli vytvářený obsah.',
     '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_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í účtů neznamená, že tato aplikace ztratí 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 svém 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.',
-    'users_api_tokens' => 'API Tokens',
-    'users_api_tokens_none' => 'No API tokens have been created for this user',
-    'users_api_tokens_create' => 'Create Token',
-    'users_api_tokens_expires' => 'Expires',
-    'users_api_tokens_docs' => 'API Documentation',
+    'users_api_tokens' => 'API Klíče',
+    'users_api_tokens_none' => 'Pro tohoto uživatele nebyly vytvořeny žádné API klíče',
+    'users_api_tokens_create' => 'Vytvořit Token',
+    'users_api_tokens_expires' => 'Vyprší',
+    'users_api_tokens_docs' => 'API Dokumentace',
 
     // API Tokens
-    'user_api_token_create' => 'Create API Token',
-    'user_api_token_name' => 'Name',
-    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
-    'user_api_token_expiry' => 'Expiry Date',
-    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
-    'user_api_token_create_success' => 'API token successfully created',
-    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token_create' => 'Vytvořit API Klíč',
+    'user_api_token_name' => 'Název',
+    'user_api_token_name_desc' => 'Zadejte srozumitelný název tokenu, který vám později může pomoci připomenout účet, za jakým jste token vytvářeli.',
+    'user_api_token_expiry' => 'Datum expirace',
+    'user_api_token_expiry_desc' => 'Zadejte datum, kdy platnost tokenu vyprší. Po tomto datu nebudou požadavky, které používají tento token, fungovat. Pokud ponecháte pole prázdné, bude tokenu nastavena platnost na dalších 100 let.',
+    'user_api_token_create_secret_message' => 'Ihned po vytvoření tokenu Vám bude vygenerován a zobrazen "Token ID" a "Token Secret". Upozorňujeme, že "Token Secret" bude možné zobrazit pouze jednou, ujistěte se, že si jej poznamenáte a uložíte na bezpečné místo před tím, než budete pokračovat dále.',
+    'user_api_token_create_success' => 'API token úspěšně vytvořen',
+    'user_api_token_update_success' => 'API token úspěšně updaten',
     'user_api_token' => 'API Token',
     'user_api_token_id' => 'Token ID',
-    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
+    'user_api_token_id_desc' => 'Toto je neupravitelný systémový identifikátor generovaný pro tento klíč, který musí být uveden v API requestu.',
     'user_api_token_secret' => 'Token Secret',
-    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
-    'user_api_token_delete' => 'Delete Token',
-    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
-    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
-    'user_api_token_delete_success' => 'API token successfully deleted',
+    'user_api_token_secret_desc' => 'Toto je systémem generovaný "secret" pro tento klíč, který musí být v API requestech. Toto bude zobrazeno pouze jednou, takže si uložte tuto hodnotu na bezpečné místo.',
+    'user_api_token_created' => 'Token vytvořen :timeAgo',
+    'user_api_token_updated' => 'Token aktualizován :timeAgo',
+    'user_api_token_delete' => 'Odstranit Token',
+    'user_api_token_delete_warning' => 'Tímto plně smažete tento API klíč s názvem \':tokenName\' ze systému.',
+    'user_api_token_delete_confirm' => 'Opravdu chcete odstranit tento API klíč?',
+    'user_api_token_delete_success' => 'API Klíč úspěšně odstraněn',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 13a8f790fc058856cbad7b2e1cb1c585815675d5..08f326d5e7a2e3fb64f16754e1b196483de3f2dd 100644 (file)
@@ -30,7 +30,7 @@ return [
     'digits'               => ':attribute musí být :digits pozic dlouhé.',
     'digits_between'       => ':attribute musí být dlouhé nejméně :min a nejvíce :max pozic.',
     'email'                => ':attribute není platný formát.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
+    'ends_with' => ':attribute musí končit jednou z následujících hodnot: :values',
     'filled'               => ':attribute musí být vyplněno.',
     'gt'                   => [
         'numeric' => ':attribute musí být větší než :value.',
@@ -46,7 +46,7 @@ return [
     ],
     'exists'               => 'Zvolená hodnota pro :attribute není platná.',
     'image'                => ':attribute musí být obrázek.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
+    'image_extension'      => ':attribute musí mít platné a podporované rozšíření obrázku.',
     'in'                   => 'Zvolená hodnota pro :attribute je neplatná.',
     'integer'              => ':attribute musí být celé číslo.',
     'ip'                   => ':attribute musí být platnou IP adresou.',
@@ -78,7 +78,7 @@ return [
         'string'  => ':attribute musí být delší než :min znaků.',
         'array'   => ':attribute musí obsahovat více než :min prvků.',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
+    'no_double_extension'  => ':attribute musí obsahovat pouze jednu příponu souboru.',
     'not_in'               => 'Zvolená hodnota pro :attribute je neplatná.',
     'not_regex'            => ':attribute musí být regulární výraz.',
     'numeric'              => ':attribute musí být číslo.',
@@ -105,7 +105,7 @@ return [
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => 'Password confirmation required',
+            'required_with' => 'Ověření hesla je vyžadováno',
         ],
     ],
 
index a78f5c1c20293437e2f8d58314effdfab3cc7f97..f060b24b8f46d9c4e14d4a6d8a1955feae37cab1 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Nulstil adgangskode',
     'reset_password_send_instructions' => 'Indtast din E-Mail herunder og du vil blive sendt en E-Mail med et link til at nulstille din adgangskode.',
     'reset_password_send_button' => 'Send link til nulstilling',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Et link til nulstilling af adgangskode sendes til :email, hvis den e-mail-adresse findes i systemet.',
     'reset_password_success' => 'Din adgangskode er blevet nulstillet.',
     'email_reset_subject' => 'Nulstil din :appName adgangskode',
     'email_reset_text' => 'Du modtager denne E-Mail fordi vi har modtaget en anmodning om at nulstille din adgangskode.',
index 008a9d24e68ae9bfc375c3ba22bcd40b4484ae72..dbd76d03c41380f891f528ace4a0f3e5b6912938 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopier',
     'reply' => 'Besvar',
     'delete' => 'Slet',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Søg',
     'search_clear' => 'Ryd søgning',
     'reset' => 'Nulstil',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => 'Profilmenu',
     'view_profile' => 'Vis profil',
     'edit_profile' => 'Redigér Profil',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Mørk tilstand',
+    'light_mode' => 'Lys tilstand',
 
     // Layout tabs
     'tab_info' => 'Info',
index 135dc9d561b592d62b7ee642a0b939d923b7e65e..28e1404cd02965dbf40d66873b629ce98e7f711f 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Indlæse mere',
     'image_image_name' => 'Billednavn',
     'image_delete_used' => 'Dette billede er brugt på siderne nedenfor.',
-    'image_delete_confirm' => 'Tryk på slet igen for at bekræft at du ønsker at slette dette billede.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Vælg billede',
     'image_dropzone' => 'Træk-og-slip billede eller klik her for at uploade',
     'images_deleted' => 'Billede slettet',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Rediger kode',
     'code_language' => 'Kodesprog',
     'code_content' => 'Kodeindhold',
+    'code_session_history' => 'Session History',
     'code_save' => 'Gem kode',
 ];
index 3a24677730dbe70acae396fc6c9b6909c507feec..a38ac83714834f42fb714982f9cb80b30117f67a 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Ingen sider matchede søgning',
     'search_for_term' => 'Søgning for :term',
     'search_more' => 'Flere resultater',
-    'search_filters' => 'Søgefiltre',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Indholdstype',
     'search_exact_matches' => 'Nøjagtige matches',
     'search_tags' => 'Tagsøgninger',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload fil',
     'attachments_link' => 'Vedhæft link',
     'attachments_set_link' => 'Sæt link',
-    'attachments_delete_confirm' => 'Tryk på slet igen for at bekræft at du ønsker at slette denne vedhæftning.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Slip filer eller klik her for at vedhæfte en fil',
     'attachments_no_files' => 'Ingen filer er blevet overført',
     'attachments_explain_link' => 'Du kan vedhæfte et link, hvis du foretrækker ikke at uploade en fil. Dette kan være et link til en anden side eller et link til en fil i skyen.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link til filen',
     'attachments_link_url_hint' => 'Adresse (URL) på side eller fil',
     'attach' => 'Vedhæft',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Rediger fil',
     'attachments_edit_file_name' => 'Filnavn',
     'attachments_edit_drop_upload' => 'Slip filer eller klik her for at uploade og overskrive',
index e5a388c33ac53d4c7c3f97fce46091d0fb156f96..0f6287fbc1861a1546fa9ada4690b0bb3a2a43db 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Filuploaden udløb.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Der blev fundet en uoverensstemmelse på siden under opdatering af vedhæftet fil',
     'attachment_not_found' => 'Vedhæftning ikke fundet',
 
     // Pages
@@ -83,7 +82,7 @@ return [
     // Error pages
     '404_page_not_found' => 'Siden blev ikke fundet',
     'sorry_page_not_found' => 'Beklager, siden du leder efter blev ikke fundet.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => 'Hvis du forventede, at denne side skulle eksistere, har du muligvis ikke tilladelse til at se den.',
     'return_home' => 'Gå tilbage til hjem',
     'error_occurred' => 'Der opstod en fejl',
     'app_down' => ':appName er nede lige nu',
index 85bd12fc319557dcc852fdacc07e882583d66be3..821d7804781ccd7b8c9581b3b9268738ec06b1f9 100644 (file)
@@ -6,7 +6,7 @@
  */
 return [
 
-    'previous' => '&laquo; Previous',
-    'next'     => 'Next &raquo;',
+    'previous' => '&laquo; Forrige',
+    'next'     => 'Næste &raquo;',
 
 ];
index 035ccb2d165bbd8b0fba07e84c01f9019b83479d..343fa2b85246d2a6ac4b45c06a5eec7c6042364e 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Adgangskoder skal være mindst otte tegn og svare til bekræftelsen.',
     'user' => "Vi kan ikke finde en bruger med den e-mail adresse.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Linket til nulstilling af adgangskode er ugyldigt for denne e-mail-adresse.',
     'sent' => 'Vi har sendt dig en e-mail med et link til at nulstille adgangskoden!',
     'reset' => 'Dit kodeord er blevet nulstillet!',
 
index b6f14a42103cbcd3153c781ea873f80773ffc947..0cb62a186875e491b1892a9bd808b37fc84c8449 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'E-Mail levering ser ud til at virke!',
     'maint_send_test_email_mail_text' => 'Tillykke! Da du har modtaget denne mailnotifikation, ser det ud som om, at dine mailindstillinger er opsat korrekt.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roller',
     'role_user_roles' => 'Brugerroller',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Tilgå system-API',
     'role_manage_settings' => 'Administrer app-indstillinger',
     'role_asset' => 'Tilladelser for medier og "assets"',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Disse tilladelser kontrollerer standardadgang til medier og "assets" i systemet. Tilladelser til bøger, kapitler og sider tilsidesætter disse tilladelser.',
     'role_asset_admins' => 'Administratorer får automatisk adgang til alt indhold, men disse indstillinger kan vise eller skjule UI-indstillinger.',
     'role_all' => 'Alle',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
@@ -192,7 +208,7 @@ return [
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
-        'he' => 'עברית',
+        'he' => 'Hebraisk',
         'hu' => 'Magyar',
         'it' => 'Italian',
         'ja' => '日本語',
index 5d0ad554a18f0939ae432c92f3dab8e825586182..fc44a9250839eb2dc54c4ab8726d8876c2d72307 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopieren',
     'reply' => 'Antworten',
     'delete' => 'Löschen',
+    'delete_confirm' => 'Löschen Bestätigen',
     'search' => 'Suchen',
     'search_clear' => 'Suche löschen',
     'reset' => 'Zurücksetzen',
index 4e56722a8cda3dac9dac2d0cb74f27281907d96b..bda1ce3768e0e0d119e367af25689ab0aa752c2a 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Mehr',
     'image_image_name' => 'Bildname',
     'image_delete_used' => 'Dieses Bild wird auf den folgenden Seiten benutzt. ',
-    'image_delete_confirm' => 'Bitte klicken Sie erneut auf löschen, wenn Sie dieses Bild wirklich entfernen möchten.',
+    'image_delete_confirm_text' => 'Möchten Sie dieses Bild wirklich löschen?',
     'image_select_image' => 'Bild auswählen',
     'image_dropzone' => 'Ziehen Sie Bilder hierher oder klicken Sie, um ein Bild auszuwählen',
     'images_deleted' => 'Bilder gelöscht',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Code editieren',
     'code_language' => 'Code Sprache',
     'code_content' => 'Code Inhalt',
+    'code_session_history' => 'Sitzungsverlauf',
     'code_save' => 'Code speichern',
 ];
index e666c664cfbcf3aa9c02105070a6cc1d9eef08e0..d6578642154d3896828c770618d5d3716c45dba9 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Keine Seiten gefunden',
     'search_for_term' => 'Nach :term suchen',
     'search_more' => 'Mehr Ergebnisse',
-    'search_filters' => 'Filter',
+    'search_advanced' => 'Erweiterte Suche',
+    'search_terms' => 'Suchbegriffe',
     'search_content_type' => 'Inhaltstyp',
     'search_exact_matches' => 'Exakte Treffer',
     'search_tags' => 'Nach Schlagwort suchen',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Datei hochladen',
     'attachments_link' => 'Link hinzufügen',
     'attachments_set_link' => 'Link setzen',
-    'attachments_delete_confirm' => 'Klicken Sie erneut auf löschen, um diesen Anhang zu entfernen.',
+    'attachments_delete' => 'Sind Sie sicher, dass Sie diesen Anhang löschen möchten?',
     'attachments_dropzone' => 'Ziehen Sie Dateien hierher oder klicken Sie, um eine Datei auszuwählen',
     'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
     'attachments_explain_link' => 'Wenn Sie keine Datei hochladen möchten, können Sie stattdessen einen Link hinzufügen. Dieser Link kann auf eine andere Seite oder eine Datei im Internet weisen.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link zu einer Datei',
     'attachments_link_url_hint' => 'URL einer Seite oder Datei',
     'attach' => 'Hinzufügen',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Datei bearbeiten',
     'attachments_edit_file_name' => 'Dateiname',
     'attachments_edit_drop_upload' => 'Ziehen Sie Dateien hierher, um diese hochzuladen und zu überschreiben',
index 205a8a6325576baf304650d634d839ba495fce6d..32be9b248f5933fe8942701363d12b3f15249a78 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Der Upload der Datei ist abgelaufen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Die Seite stimmte nach dem Hochladen des Anhangs nicht überein.',
     'attachment_not_found' => 'Anhang konnte nicht gefunden werden.',
 
     // Pages
index d8ffedbf2ee00c9ea40bda55215471f8f128c79f..66307fab148dfdee2330003bbf3f4411d0466a50 100644 (file)
@@ -84,6 +84,20 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
     'maint_send_test_email_mail_text' => 'Glückwunsch! Da Sie diese E-Mail Benachrichtigung erhalten haben, scheinen Ihre E-Mail-Einstellungen korrekt konfiguriert zu sein.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Rollen',
     'role_user_roles' => 'Benutzer-Rollen',
@@ -109,6 +123,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'role_access_api' => 'Systemzugriffs-API',
     'role_manage_settings' => 'Globaleinstellungen verwalten',
     'role_asset' => 'Berechtigungen',
+    'roles_system_warning' => 'Beachten Sie, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weisen Sie nur Rollen, mit diesen Berechtigungen, vertrauenswürdigen Benutzern zu.',
     'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
     'role_asset_admins' => 'Administratoren erhalten automatisch Zugriff auf alle Inhalte, aber diese Optionen können Oberflächenoptionen ein- oder ausblenden.',
     'role_all' => 'Alle',
@@ -188,6 +203,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
index c54b23afc1d0cdac35e5b866d1acb681cc4053c2..c18f786f61c495c7728372ea4a55861ad97c3dd9 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopieren',
     'reply' => 'Antworten',
     'delete' => 'Löschen',
+    'delete_confirm' => 'Löschen Bestätigen',
     'search' => 'Suchen',
     'search_clear' => 'Suche löschen',
     'reset' => 'Zurücksetzen',
index 4d98235a4370b6a243b7ec90ee3cd7553232bc2c..56060ea236e1e30f6eeacfc44622cdc24f0e29ec 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Mehr',
     'image_image_name' => 'Bildname',
     'image_delete_used' => 'Dieses Bild wird auf den folgenden Seiten benutzt. ',
-    'image_delete_confirm' => 'Bitte klicke erneut auf löschen, wenn Du dieses Bild wirklich entfernen möchtest.',
+    'image_delete_confirm_text' => 'Bist Du sicher, dass Du diese Seite löschen möchtest?',
     'image_select_image' => 'Bild auswählen',
     'image_dropzone' => 'Ziehe Bilder hierher oder klicke hier, um ein Bild auszuwählen',
     'images_deleted' => 'Bilder gelöscht',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Code editieren',
     'code_language' => 'Code Sprache',
     'code_content' => 'Code Inhalt',
+    'code_session_history' => 'Sitzungsverlauf',
     'code_save' => 'Code speichern',
 ];
index 3888035c315af5b7730c4e2e740df4ccfc69ace9..a1b628b89f9db13d42f8af0199bdc05556ba90d4 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Keine Seiten gefunden',
     'search_for_term' => 'Nach :term suchen',
     'search_more' => 'Mehr Ergebnisse',
-    'search_filters' => 'Filter',
+    'search_advanced' => 'Erweiterte Suche',
+    'search_terms' => 'Suchbegriffe',
     'search_content_type' => 'Inhaltstyp',
     'search_exact_matches' => 'Exakte Treffer',
     'search_tags' => 'Nach Schlagwort suchen',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Datei hochladen',
     'attachments_link' => 'Link hinzufügen',
     'attachments_set_link' => 'Link setzen',
-    'attachments_delete_confirm' => 'Klicke erneut auf löschen, um diesen Anhang zu entfernen.',
+    'attachments_delete' => 'Bist Du sicher, dass Du diesen Anhang löschen möchtest?',
     'attachments_dropzone' => 'Ziehe Dateien hierher oder klicke hier, um eine Datei auszuwählen',
     'attachments_no_files' => 'Es wurden bisher keine Dateien hochgeladen.',
     'attachments_explain_link' => 'Wenn Du keine Datei hochladen möchtest, kannst Du stattdessen einen Link hinzufügen. Dieser Link kann auf eine andere Seite oder eine Datei im Internet verweisen.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link zu einer Datei',
     'attachments_link_url_hint' => 'URL einer Seite oder Datei',
     'attach' => 'Hinzufügen',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Datei bearbeiten',
     'attachments_edit_file_name' => 'Dateiname',
     'attachments_edit_drop_upload' => 'Ziehe Dateien hierher, um diese hochzuladen und zu überschreiben',
index 3707dbf13768e2647f41ebb5abcffa49c057fa48..656c3c23647133f1d12b38e0714bc68dd48f9a7c 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Der Upload der Datei ist abgelaufen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Die Seite stimmte nach dem Hochladen des Anhangs nicht überein.',
     'attachment_not_found' => 'Anhang konnte nicht gefunden werden.',
 
     // Pages
index 3da092cb8c7e234a0f5a852afb272e183a5999d9..0dd28c019f13452473408060a81110c5806da8a5 100644 (file)
@@ -8,8 +8,8 @@ return [
 
     'password' => 'Passwörter müssen aus mindestens sechs Zeichen bestehen und mit der eingegebenen Wiederholung übereinstimmen.',
     'user' => "Es wurde kein Benutzer mit dieser E-Mail-Adresse gefunden.",
-    'token' => 'Der Link zum Zurücksetzen Ihres Passworts ist entweder ungültig oder abgelaufen.',
-    'sent' => 'Der Link zum Zurücksetzen Ihres Passwortes wurde Ihnen per E-Mail zugesendet.',
-    'reset' => 'Ihr Passwort wurde zurückgesetzt!',
+    'token' => 'Der Token zum Zurücksetzen des Passworts für diese E-Mail-Adresse ist ungültig.',
+    'sent' => 'Wir haben dir einen Link zum Zurücksetzen des Passwortes per E-Mail geschickt!',
+    'reset' => 'Dein Passwort wurde zurückgesetzt!',
 
 ];
index a39e00e04ccb40c966255818bd0ddbf6b63af74e..a0ddc5a04a8db1e4bce11ff844dd84af8261908d 100644 (file)
@@ -84,6 +84,20 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'maint_send_test_email_mail_greeting' => 'E-Mail-Versand scheint zu funktionieren!',
     'maint_send_test_email_mail_text' => 'Glückwunsch! Da du diese E-Mail Benachrichtigung erhalten hast, scheinen deine E-Mail-Einstellungen korrekt konfiguriert zu sein.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Rollen',
     'role_user_roles' => 'Benutzer-Rollen',
@@ -109,6 +123,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'role_access_api' => 'Systemzugriffs-API',
     'role_manage_settings' => 'Globaleinstellungen verwalten',
     'role_asset' => 'Berechtigungen',
+    'roles_system_warning' => 'Beachten Sie, dass der Zugriff auf eine der oben genannten drei Berechtigungen einem Benutzer erlauben kann, seine eigenen Berechtigungen oder die Rechte anderer im System zu ändern. Weisen Sie nur Rollen, mit diesen Berechtigungen, vertrauenswürdigen Benutzern zu.',
     'role_asset_desc' => 'Diese Berechtigungen gelten für den Standard-Zugriff innerhalb des Systems. Berechtigungen für Bücher, Kapitel und Seiten überschreiben diese Berechtigungenen.',
     'role_asset_admins' => 'Administratoren erhalten automatisch Zugriff auf alle Inhalte, aber diese Optionen können Oberflächenoptionen ein- oder ausblenden.',
     'role_all' => 'Alle',
@@ -188,6 +203,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
@@ -195,7 +211,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
-        'he' => 'Hebräisch',
+        'he' => 'עברית',
         'hu' => 'Magyar',
         'it' => 'Italian',
         'ja' => '日本語',
index 68c58b92ba4e9243fd1afae7eca30ed89feab4df..e87bd11a5e343173fadf78e62a042e1ca0579a4a 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copy',
     'reply' => 'Reply',
     'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
     'reset' => 'Reset',
index d8e8981fb5fcf6ba8d15993453d4f8f2d07df970..48a0a32faa38c4821a9d71dda9a5fb4f97d35232 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Load More',
     'image_image_name' => 'Image Name',
     'image_delete_used' => 'This image is used in the pages below.',
-    'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Select Image',
     'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edit Code',
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
     'code_save' => 'Save Code',
 ];
index 6bbc723b0abfc1e6b1f69e270bbb9d2afdbe851b..f64867a56c31736a1730d58c51f3fe0c088364d1 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'No pages matched this search',
     'search_for_term' => 'Search for :term',
     'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Content Type',
     'search_exact_matches' => 'Exact Matches',
     'search_tags' => 'Tag Searches',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload File',
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'Url of site or file',
     'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
index 06a5285f56fc4ce11e6642549a1002b1bacae698..79024e482ed69efa633116592f9b7c83a0bcc93a 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
index f1345c743b6dcc2bdfc7555774627195ebcd4109..e280396a25fb136154aabf2552a364e59fa99476 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'User Roles',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Manage app settings',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'All',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 4f4f7f6001ee7663e7a6bb8ff250302582125933..9c6575a5381a3769119162dc292c9accbd52236d 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Borrar',
+    'delete_confirm' => 'Confirmar borrado',
     'search' => 'Buscar',
     'search_clear' => 'Limpiar búsqueda',
     'reset' => 'Resetear',
index f9251991216e07493e3438a2502a30bf5b264bc2..fb4929ad4682313af337e76df503b64342dfdb61 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Cargar más',
     'image_image_name' => 'Nombre de imagen',
     'image_delete_used' => 'Esta imagen está siendo utilizada en las páginas mostradas a continuación.',
-    'image_delete_confirm' => 'Haga click de nuevo para confirmar que quiere borrar esta imagen.',
+    'image_delete_confirm_text' => '¿Estás seguro de que quieres eliminar esta imagen?',
     'image_select_image' => 'Seleccionar Imagen',
     'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
     'images_deleted' => 'Imágenes borradas',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Lenguaje del Código',
     'code_content' => 'Contenido del Código',
+    'code_session_history' => 'Historial de la sesión',
     'code_save' => 'Guardar Código',
 ];
index f67a0a3a3a68ad208a34bfd31fe384b995a08adc..aac632419aa33163b92e3952161516a23a9f717b 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
     'search_for_term' => 'Búsqueda por :term',
     'search_more' => 'Más Resultados',
-    'search_filters' => 'Filtros de Búsqueda',
+    'search_advanced' => 'Búsqueda Avanzada',
+    'search_terms' => 'Términos de búsqueda',
     'search_content_type' => 'Tipo de Contenido',
     'search_exact_matches' => 'Coincidencias Exactas',
     'search_tags' => 'Búsquedas Etiquetadas',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Subir Archivo',
     'attachments_link' => 'Adjuntar Enlace',
     'attachments_set_link' => 'Ajustar Enlace',
-    'attachments_delete_confirm' => 'Haga click en borrar nuevamente para confirmar que quiere borrar este adjunto.',
+    'attachments_delete' => '¿Está seguro de que quiere eliminar este archivo adjunto?',
     'attachments_dropzone' => 'Arrastre ficheros aquí o haga click aquí para adjuntar un fichero',
     'attachments_no_files' => 'No se han subido ficheros',
     'attachments_explain_link' => 'Puede agregar un enlace si prefiere no subir un archivo. Puede ser un enlace a otra página o un enlace a un fichero en la nube.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Enlace a fichero',
     'attachments_link_url_hint' => 'Url del sitio o fichero',
     'attach' => 'Adjuntar',
+    'attachments_insert_link' => 'Añadir enlace al adjunto en la página',
     'attachments_edit_file' => 'Editar fichero',
     'attachments_edit_file_name' => 'Nombre del fichero',
     'attachments_edit_drop_upload' => 'Arrastre a los ficheros o haga click aquí para subir y sobreescribir',
index ce69b6157f3720dedb085b8a3e43064e7b46b7f8..f83ec4b80d77755158d06acbd5dc5d8cf2c25681 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'La carga del archivo ha caducado.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Página no coincidente durante la subida del adjunto ',
     'attachment_not_found' => 'No se encontró el adjunto',
 
     // Pages
index 7f249b04e538b97c75fc251c432c138a980eaa1e..48473acef1c09333725d0eb78b29e10163c6acfb 100644 (file)
@@ -13,12 +13,12 @@ return [
 
     // App Settings
     'app_customization' => 'Personalización',
-    'app_features_security' => 'Características & Seguridad',
+    'app_features_security' => 'Características y seguridad',
     'app_name' => 'Nombre de la aplicación',
-    'app_name_desc' => 'Este nombre se muestra en la cabecera y en cualquier correo electrónico',
-    'app_name_header' => 'Mostrar el nombre de la aplicación en la cabecera',
-    'app_public_access' => 'Acceso Público',
-    'app_public_access_desc' => 'Activando esta opción permitirá que usuarios sin iniciar sesión puedan ver el contenido de tu aplicación Bookstack.',
+    'app_name_desc' => 'Este nombre se muestra en la cabecera y en cualquier correo electrónico enviado por el sistema.',
+    'app_name_header' => 'Mostrar nombre en la cabecera',
+    'app_public_access' => 'Acceso público',
+    'app_public_access_desc' => 'Activar esta opción permitirá a los visitantes que no hayan iniciado sesión, poder ver el contenido de tu BookStack.',
     'app_public_access_desc_guest' => 'El acceso público para visitantes puede ser controlado a través del usuario "Guest".',
     'app_public_access_toggle' => 'Permitir acceso público',
     'app_public_viewing' => '¿Permitir acceso público?',
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
     'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
 
+    // Audit Log
+    'audit' => 'Registro de Auditoría',
+    'audit_desc' => 'Este registro de auditoría muestra una lista de actividades registradas en el sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+    'audit_event_filter' => 'Filtro de eventos',
+    'audit_event_filter_no_filter' => 'Sin filtro',
+    'audit_deleted_item' => 'Elemento eliminado',
+    'audit_deleted_item_name' => 'Nombre: :name',
+    'audit_table_user' => 'Usuario',
+    'audit_table_event' => 'Evento',
+    'audit_table_item' => 'Elemento relacionado',
+    'audit_table_date' => 'Fecha de la actividad',
+    'audit_date_from' => 'Rango de fecha desde',
+    'audit_date_to' => 'Rango de fecha hasta',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'Roles de usuario',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'API de sistema de acceso',
     'role_manage_settings' => 'Gestionar ajustes de la aplicación',
     'role_asset' => 'Permisos de contenido',
+    'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario alterar sus propios privilegios o los privilegios de otros en el sistema. Sólo asignar roles con estos permisos a usuarios de confianza.',
     'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los contenidos del sistema. Los permisos de Libros, Capítulos y Páginas sobreescribiran estos permisos.',
     'role_asset_admins' => 'A los administradores se les asigna automáticamente permisos para acceder a todo el contenido pero estas opciones podrían mostrar u ocultar opciones de la interfaz.',
     'role_all' => 'Todo',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Dale a tu token un nombre legible como un recordatorio futuro de su propósito.',
     'user_api_token_expiry' => 'Fecha de expiración',
     'user_api_token_expiry_desc' => 'Establece una fecha en la que este token expira. Después de esta fecha, las solicitudes realizadas usando este token ya no funcionarán. Dejar este campo en blanco fijará un vencimiento de 100 años en el futuro.',
-    'user_api_token_create_secret_message' => 'Inmediatamente después de crear este token se generarán y mostrarán sus correspondientes "Token ID" y "Token Secret". El "Token Secret" sólo se mostrará una vez, así que asegúrese de copiar el valor a un lugar seguro antes de proceder.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'Token API creado correctamente',
     'user_api_token_update_success' => 'Token API actualizado correctamente',
     'user_api_token' => 'Token API',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'Este es un identificador no editable generado por el sistema y único para este token que necesitará ser proporcionado en solicitudes de API.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'Esta es una clave no editable generada por el sistema que necesitará ser proporcionada en solicitudes de API. Solo se monstraré esta vez así que guarde su valor en un lugar seguro.',
-    'user_api_token_created' => 'Token creado :timeAgo',
-    'user_api_token_updated' => 'Token actualizado :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Borrar token',
     'user_api_token_delete_warning' => 'Esto eliminará completamente este token API con el nombre \':tokenName\' del sistema.',
     'user_api_token_delete_confirm' => '¿Está seguro de que desea borrar este API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Danés',
         'de' => 'Deutsch (Sie)',
index 8345110413b27748526797f0991d8ff17d8e0a6a..2f957f46d6f18d108cc5453f4b8faec60483efd4 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Restablecer la contraseña',
     'reset_password_send_instructions' => 'Introduzca su correo electrónico a continuación y se le enviará un correo electrónico con un enlace para la restauración',
     'reset_password_send_button' => 'Enviar enlace de restauración',
-    'reset_password_sent' => 'Un enlace para cambiar la contraseña será enviado a su dirección de correo electrónico si existe en nuestro sistema.',
+    'reset_password_sent' => 'Si la dirección de correo electrónico :email existe en el sistema, se enviará un enlace para restablecer la contraseña.',
     'reset_password_success' => 'Su contraseña se restableció con éxito.',
     'email_reset_subject' => 'Restauración de la contraseña de para la aplicación :appName',
     'email_reset_text' => 'Ud. esta recibiendo este correo electrónico debido a que recibimos una solicitud de restauración de la contraseña de su cuenta.',
index 0051e9c2247a2e7a8395fd5f2d7ac21be726079a..c6bef56bce6884c58635641f6d105495a37e4d3b 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Borrar',
+    'delete_confirm' => 'Confirmar eliminación',
     'search' => 'Buscar',
     'search_clear' => 'Limpiar búsqueda',
     'reset' => 'Restablecer',
index d205afbc115d94ea6ebe03b1415cb52f841b5113..f7be885898a4ac7b6d5374b2bd55f5afac2f7286 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Cargar más',
     'image_image_name' => 'Nombre de imagen',
     'image_delete_used' => 'Esta imagen esta siendo utilizada en las páginas a continuación.',
-    'image_delete_confirm' => 'Haga click de nuevo para confirmar que quiere borrar esta imagen.',
+    'image_delete_confirm_text' => '¿Está seguro que quiere eliminar esta imagen?',
     'image_select_image' => 'Seleccionar Imagen',
     'image_dropzone' => 'Arrastre las imágenes o hacer click aquí para Subir',
     'images_deleted' => 'Imágenes borradas',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Lenguaje del Código',
     'code_content' => 'Contenido del Código',
+    'code_session_history' => 'Historial de la sesión',
     'code_save' => 'Guardar Código',
 ];
index 700e873c31d1d0c5786c14867b7b5370116bdc5f..3cd205ad9f76ce0af388d970900e38ca9e362d57 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Ninguna página encontrada para la búsqueda',
     'search_for_term' => 'Busqueda por :term',
     'search_more' => 'Más resultados',
-    'search_filters' => 'Filtros de búsqueda',
+    'search_advanced' => 'Búsqueda Avanzada',
+    'search_terms' => 'Términos de búsqueda',
     'search_content_type' => 'Tipo de contenido',
     'search_exact_matches' => 'Coincidencias exactas',
     'search_tags' => 'Búsquedas de etiquetas',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Archivo adjuntado',
     'attachments_link' => 'Adjuntar enlace',
     'attachments_set_link' => 'Establecer enlace',
-    'attachments_delete_confirm' => 'Presione en borrar nuevamente para confirmar que quiere borrar este elemento adjunto.',
+    'attachments_delete' => '¿Está seguro que desea eliminar el archivo adjunto?',
     'attachments_dropzone' => 'Arrastre archivos aquí o presione aquí para adjuntar un archivo',
     'attachments_no_files' => 'No se adjuntó ningún archivo',
     'attachments_explain_link' => 'Usted puede agregar un enlace o si lo prefiere puede agregar un archivo. Esto puede ser un enlace a otra página o un enlace a un archivo en la nube.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Enlace a archivo',
     'attachments_link_url_hint' => 'URL del sitio o archivo',
     'attach' => 'Adjuntar',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Editar archivo',
     'attachments_edit_file_name' => 'Nombre del archivo',
     'attachments_edit_drop_upload' => 'Arrastre los archivos o presione aquí para subir o sobreescribir',
index c640492fecda756ef69eb7b994d8d3ca8ff95522..41719a5bcb3c035c553eada751bd4e93eebf8a48 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'La carga del archivo ha caducado.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Página no coincidente durante la subida del adjunto ',
     'attachment_not_found' => 'No se encuentra el objeto adjunto',
 
     // Pages
index ab386c0d7e5944a2d32ee93c2edf09a39ac0575b..f9e24939b2a130c19385f7eb6c69544dcbd68845 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'La contraseña debe ser como mínimo de seis caracteres y coincidir con la confirmación.',
     'user' => "No podemos encontrar un usuario con esta dirección de correo electrónico.",
-    'token' => 'El token de modificación de contraseña no es válido para esta dirección de correo electrónico.',
+    'token' => 'El token para restablecer la contraseña no es válido para esta dirección de correo electrónico.',
     'sent' => '¡Hemos enviado a su cuenta de correo electrónico un enlace para restaurar su contraseña!',
     'reset' => '¡Su contraseña fue restaurada!',
 
index 9a27eedfdc103a524ad20933a3bf9770ada7c16c..641b10c2898fb53a1d62d07bf82832a9c22ae6b2 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => '¡El envío de correos electrónicos parece funcionar!',
     'maint_send_test_email_mail_text' => '¡Enhorabuena! Al recibir esta notificación de correo electrónico, tu configuración de correo electrónico parece estar ajustada correctamente.',
 
+    // Audit Log
+    'audit' => 'Registro de Auditoría',
+    'audit_desc' => 'Este registro de auditoría muestra una lista de actividades registradas en el sistema. Esta lista no está filtrada a diferencia de las listas de actividad similares en el sistema donde se aplican los filtros de permisos.',
+    'audit_event_filter' => 'Filtro de eventos',
+    'audit_event_filter_no_filter' => 'Sin filtro',
+    'audit_deleted_item' => 'Elemento eliminado',
+    'audit_deleted_item_name' => 'Nombre: :name',
+    'audit_table_user' => 'Usuario',
+    'audit_table_event' => 'Evento',
+    'audit_table_item' => 'Elemento relacionado',
+    'audit_table_date' => 'Fecha de la actividad',
+    'audit_date_from' => 'Rango de fecha desde',
+    'audit_date_to' => 'Rango de fecha hasta',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'Roles de usuario',
@@ -107,6 +121,7 @@ return [
     'role_access_api' => 'API de sistema de acceso',
     'role_manage_settings' => 'Gestionar ajustes de activos',
     'role_asset' => 'Permisos de activos',
+    'roles_system_warning' => 'Tenga en cuenta que el acceso a cualquiera de los tres permisos anteriores puede permitir a un usuario alterar sus propios privilegios o los privilegios de otros en el sistema. Sólo asignar roles con estos permisos a usuarios de confianza.',
     'role_asset_desc' => 'Estos permisos controlan el acceso por defecto a los activos del sistema. Permisos a Libros, Capítulos y Páginas sobreescribiran estos permisos.',
     'role_asset_admins' => 'Los administradores reciben automáticamente acceso a todo el contenido pero estas opciones pueden mostrar u ocultar opciones de UI.',
     'role_all' => 'Todo',
@@ -165,16 +180,16 @@ return [
     'user_api_token_name_desc' => 'Dale a tu token un nombre legible como un recordatorio futuro de su propósito.',
     'user_api_token_expiry' => 'Fecha de expiración',
     'user_api_token_expiry_desc' => 'Establece una fecha en la que este token expira. Después de esta fecha, las solicitudes realizadas usando este token ya no funcionarán. Dejar este campo en blanco fijará un vencimiento de 100 años en el futuro.',
-    'user_api_token_create_secret_message' => 'Inmediatamente después de crear este token se generarán y mostrarán sus correspondientes "Token ID" y "Token Secret". El "Token Secret" sólo se mostrará una vez, así que asegúrese de copiar el valor a un lugar seguro antes de proceder.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'Token API creado correctamente',
     'user_api_token_update_success' => 'Token API actualizado correctamente',
     'user_api_token' => 'Token API',
     'user_api_token_id' => 'Token ID',
     'user_api_token_id_desc' => 'Este es un identificador no editable generado por el sistema y único para este token que necesitará ser proporcionado en solicitudes de API.',
-    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret' => 'Clave de Token',
     'user_api_token_secret_desc' => 'Esta es una clave no editable generada por el sistema que necesitará ser proporcionada en solicitudes de API. Solo se monstraré esta vez así que guarde su valor en un lugar seguro.',
-    'user_api_token_created' => 'Token creado :timeAgo',
-    'user_api_token_updated' => 'Token actualizado :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Borrar token',
     'user_api_token_delete_warning' => 'Esto eliminará completamente este token API con el nombre \':tokenName\' del sistema.',
     'user_api_token_delete_confirm' => '¿Está seguro de que desea borrar este API token?',
@@ -186,6 +201,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Danés',
         'de' => 'Deutsch (Sie)',
index 68c58b92ba4e9243fd1afae7eca30ed89feab4df..e87bd11a5e343173fadf78e62a042e1ca0579a4a 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copy',
     'reply' => 'Reply',
     'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
     'reset' => 'Reset',
index d8e8981fb5fcf6ba8d15993453d4f8f2d07df970..48a0a32faa38c4821a9d71dda9a5fb4f97d35232 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Load More',
     'image_image_name' => 'Image Name',
     'image_delete_used' => 'This image is used in the pages below.',
-    'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Select Image',
     'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edit Code',
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
     'code_save' => 'Save Code',
 ];
index 6bbc723b0abfc1e6b1f69e270bbb9d2afdbe851b..f64867a56c31736a1730d58c51f3fe0c088364d1 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'No pages matched this search',
     'search_for_term' => 'Search for :term',
     'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Content Type',
     'search_exact_matches' => 'Exact Matches',
     'search_tags' => 'Tag Searches',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload File',
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'Url of site or file',
     'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
index 06a5285f56fc4ce11e6642549a1002b1bacae698..79024e482ed69efa633116592f9b7c83a0bcc93a 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
index f1345c743b6dcc2bdfc7555774627195ebcd4109..2bd314cf0f28561f9a2f296b373483df42480f89 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'User Roles',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Manage app settings',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'All',
index 81dde84c47dc1f6be7651b1c93048bab58429eb6..6571db60c6d37485f5476e9123da9b5ccfb00b43 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copier',
     'reply' => 'Répondre',
     'delete' => 'Supprimer',
+    'delete_confirm' => 'Confirmer la suppression',
     'search' => 'Chercher',
     'search_clear' => 'Réinitialiser la recherche',
     'reset' => 'Réinitialiser',
index 2f6ff8bf9c906413e5a679c4943b6364d9ea8633..6cce4f80418f1e5967daceee7b8e72b30b0f28a6 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Charger plus',
     'image_image_name' => 'Nom de l\'image',
     'image_delete_used' => 'Cette image est utilisée dans les pages ci-dessous.',
-    'image_delete_confirm' => 'Confirmez que vous souhaitez bien supprimer cette image.',
+    'image_delete_confirm_text' => 'Êtes-vous sûr de vouloir supprimer cette image ?',
     'image_select_image' => 'Sélectionner l\'image',
     'image_dropzone' => 'Glissez les images ici ou cliquez pour les ajouter',
     'images_deleted' => 'Images supprimées',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editer le code',
     'code_language' => 'Langage du code',
     'code_content' => 'Contenu du code',
+    'code_session_history' => 'Historique de session',
     'code_save' => 'Enregistrer le code',
 ];
index d52dbfda348dc4842388531caa33413d2c538b9c..bc37fa52cbd633a9fa3e4a5071d1467066c2f8c2 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Aucune page correspondant à cette recherche',
     'search_for_term' => 'recherche pour :term',
     'search_more' => 'Plus de résultats',
-    'search_filters' => 'Filtres de recherche',
+    'search_advanced' => 'Recherche avancée',
+    'search_terms' => 'Mot-clé',
     'search_content_type' => 'Type de contenu',
     'search_exact_matches' => 'Correspondances exactes',
     'search_tags' => 'Recherche par tags',
@@ -87,7 +88,7 @@ return [
     'shelves_edit' => 'Modifier l\'étagère',
     'shelves_delete' => 'Supprimer l\'étagère',
     'shelves_delete_named' => 'Supprimer l\'étagère :name',
-    'shelves_delete_explain' => "Ceci va supprimer l\\'étagère nommée \\':bookName\\'. Les livres contenus dans cette étagère ne seront pas supprimés.",
+    'shelves_delete_explain' => "Ceci va supprimer l'étagère nommée ':name'. Les livres contenus dans cette étagère ne seront pas supprimés.",
     'shelves_delete_confirmation' => 'Êtes-vous sûr(e) de vouloir supprimer cette étagère ?',
     'shelves_permissions' => 'Permissions de l\'étagère',
     'shelves_permissions_updated' => 'Permissions de l\'étagère mises à jour',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Uploader un fichier',
     'attachments_link' => 'Attacher un lien',
     'attachments_set_link' => 'Définir un lien',
-    'attachments_delete_confirm' => 'Cliquer une seconde fois sur supprimer pour valider la suppression.',
+    'attachments_delete' => 'Êtes-vous sûr de vouloir supprimer la pièce jointe ?',
     'attachments_dropzone' => 'Glissez des fichiers ou cliquez ici pour attacher des fichiers',
     'attachments_no_files' => 'Aucun fichier ajouté',
     'attachments_explain_link' => 'Vous pouvez attacher un lien si vous ne souhaitez pas uploader un fichier.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Lien sur un fichier',
     'attachments_link_url_hint' => 'URL du site ou du fichier',
     'attach' => 'Attacher',
+    'attachments_insert_link' => 'Ajouter un lien de pièce jointe à la page',
     'attachments_edit_file' => 'Modifier le fichier',
     'attachments_edit_file_name' => 'Nom du fichier',
     'attachments_edit_drop_upload' => 'Glissez un fichier ou cliquer pour mettre à jour le fichier',
index 2c697e67df9d21adc094981e865d33babe89e65e..c1c14fe31f36ed8313026824792f19e2577cda8f 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Le téléchargement du fichier a expiré.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page incorrecte durant la mise à jour du fichier joint',
     'attachment_not_found' => 'Fichier joint non trouvé',
 
     // Pages
index d2096ed75530d054d546d5c22e97a6b4ef8ecc83..0a24f9cded12e6b6bb873748bae6914f7c875f64 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'La livraison d\'email semble fonctionner !',
     'maint_send_test_email_mail_text' => 'Félicitations ! Lorsque vous avez reçu cette notification par courriel, vos paramètres d\'email semblent être configurés correctement.',
 
+    // Audit Log
+    'audit' => 'Journal d\'audit',
+    'audit_desc' => 'Ce journal d\'audit affiche une liste des activités suivies dans le système. Cette liste n\'est pas filtrée contrairement aux listes d\'activités similaires dans le système où les filtres d\'autorisation sont appliqués.',
+    'audit_event_filter' => 'Filtres d\'événement',
+    'audit_event_filter_no_filter' => 'Pas de filtre',
+    'audit_deleted_item' => 'Élément supprimé',
+    'audit_deleted_item_name' => 'Nom: :name',
+    'audit_table_user' => 'Utilisateur',
+    'audit_table_event' => 'Evènement',
+    'audit_table_item' => 'Élément Associé',
+    'audit_table_date' => 'Date d\'activation',
+    'audit_date_from' => 'À partir du',
+    'audit_date_to' => 'Jusqu\'au',
+
     // Role Settings
     'roles' => 'Rôles',
     'role_user_roles' => 'Rôles des utilisateurs',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Accès à l\'API du système',
     'role_manage_settings' => 'Gérer les préférences de l\'application',
     'role_asset' => 'Permissions des ressources',
+    'roles_system_warning' => 'Sachez que l\'accès à l\'une des trois permissions ci-dessus peut permettre à un utilisateur de modifier ses propres privilèges ou les privilèges des autres utilisateurs du système. Attribuer uniquement des rôles avec ces permissions à des utilisateurs de confiance.',
     'role_asset_desc' => 'Ces permissions contrôlent l\'accès par défaut des ressources dans le système. Les permissions dans les livres, les chapitres et les pages ignoreront ces permissions',
     'role_asset_admins' => 'Les administrateurs ont automatiquement accès à tous les contenus mais les options suivantes peuvent afficher ou masquer certaines options de l\'interface.',
     'role_all' => 'Tous',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bulgare',
         'cs' => 'Česky',
         'da' => 'Danois',
         'de' => 'Deutsch (Sie)',
index 0dc1cc43dcdc2c93c24a32a2e899328c0f9b7e74..8a6311abdcfdbcd0266c413850dddaff20685a99 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'העתק',
     'reply' => 'השב',
     'delete' => 'מחק',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'חיפוש',
     'search_clear' => 'נקה חיפוש',
     'reset' => 'איפוס',
index 73dbe4e18ac65f419c5cc3bb6b02bc0c70d7c721..84ff7c3105cb343ab90cc02ac2cd48bbc5986123 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'טען עוד',
     'image_image_name' => 'שם התמונה',
     'image_delete_used' => 'תמונה זו בשימוש בדפים שמתחת',
-    'image_delete_confirm' => 'לחץ ״מחק״ שוב על מנת לאשר שברצונך למחוק תמונה זו',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'בחר תמונה',
     'image_dropzone' => 'גרור תמונות או לחץ כאן להעלאה',
     'images_deleted' => 'התמונות נמחקו',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'ערוך קוד',
     'code_language' => 'שפת הקוד',
     'code_content' => 'תוכן הקוד',
+    'code_session_history' => 'Session History',
     'code_save' => 'שמור קוד',
 ];
index 2fb0e82ec663d7ee3438b45bf1fd49f7e45afc9e..8eef64efa390280aa652fc7fa759b68f32321534 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'לא נמצאו דפים התואמים לחיפוש',
     'search_for_term' => 'חפש את :term',
     'search_more' => 'תוצאות נוספות',
-    'search_filters' => 'מסנני חיפוש',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'סוג התוכן',
     'search_exact_matches' => 'התאמות מדויקות',
     'search_tags' => 'חפש בתגים',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'העלה קובץ',
     'attachments_link' => 'צרף קישור',
     'attachments_set_link' => 'הגדר קישור',
-    'attachments_delete_confirm' => 'יש ללחוץ שוב על מחיקה על מנת לאשר את מחיקת הקובץ המצורף',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'גרור לכאן קבצים או לחץ על מנת לצרף קבצים',
     'attachments_no_files' => 'לא הועלו קבצים',
     'attachments_explain_link' => 'ניתן לצרף קישור במקום העלאת קובץ, קישור זה יכול להוביל לדף אחר או לכל קובץ באינטרנט',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'קישור לקובץ',
     'attachments_link_url_hint' => 'כתובת האתר או הקובץ',
     'attach' => 'צרף',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'ערוך קובץ',
     'attachments_edit_file_name' => 'שם הקובץ',
     'attachments_edit_drop_upload' => 'גרור קבצים או לחץ כאן על מנת להעלות קבצים במקום הקבצים הקיימים',
index 9920f1b9880b8b61b1150dd897b4a3ea1c268662..f6a01d3a3ff4d9c94ddc1c2ec980793cb0b63530 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'קובץ מצורף לא נמצא',
 
     // Pages
index e3cd0ead5085514c089b671e5dd9b7054099827e..c90e834b350fa380f2811a39913aa5c4de0970aa 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'תפקידים',
     'role_user_roles' => 'תפקידי משתמשים',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'ניהול הגדרות יישום',
     'role_asset' => 'הרשאות משאבים',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'הרשאות אלו שולטות בגישת ברירת המחדל למשאבים בתוך המערכת. הרשאות של ספרים, פרקים ודפים יגברו על הרשאות אלו.',
     'role_asset_admins' => 'מנהלים מקבלים הרשאה מלאה לכל התוכן אך אפשרויות אלו עלולות להציג או להסתיר אפשרויות בממשק',
     'role_all' => 'הכל',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 3abd3bf2761819b5807df503e0bf974ba72c4483..51a6476d08d3b1de60fa8f41d8c77ed78df78cb6 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Másolás',
     'reply' => 'Válasz',
     'delete' => 'Törlés',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Keresés',
     'search_clear' => 'Keresés törlése',
     'reset' => 'Visszaállítás',
index 1f98df2dfe150dedf85fe7f35145df7782510edd..80afacf33cc4458fef11941dd7343643bb586894 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Több betöltése',
     'image_image_name' => 'Kép neve',
     'image_delete_used' => 'Ez a kép a lenti oldalakon van használatban.',
-    'image_delete_confirm' => 'A kép törléséhez ismét rá kell kattintani a törlésre.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Kép kiválasztása',
     'image_dropzone' => 'Képek feltöltése ejtéssel vagy kattintással',
     'images_deleted' => 'Képek törölve',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Kód szerkesztése',
     'code_language' => 'Kód nyelve',
     'code_content' => 'Kód tartalom',
+    'code_session_history' => 'Session History',
     'code_save' => 'Kód mentése',
 ];
index 6593212f0b8c86f764428e6f1c096be07f328110..b580d0b028bc00fc21b8e8ca0572db7488a81d29 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Nincsenek a keresésnek megfelelő oldalak',
     'search_for_term' => ':term keresése',
     'search_more' => 'További eredmények',
-    'search_filters' => 'Keresési szűrők',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Tartalomtípus',
     'search_exact_matches' => 'Pontos egyezések',
     'search_tags' => 'Címke keresések',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Fájlfeltöltés',
     'attachments_link' => 'Hivatkozás csatolása',
     'attachments_set_link' => 'Hivatkozás beállítása',
-    'attachments_delete_confirm' => 'A csatolmány törléséhez ismét rá kell kattintani a törlésre.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Fájlok csatolása ejtéssel vagy kattintással',
     'attachments_no_files' => 'Nincsenek fájlok feltöltve',
     'attachments_explain_link' => 'Fájl feltöltése helyett hozzá lehet kapcsolni egy hivatkozást. Ez egy hivatkozás lesz egy másik oldalra vagy egy fájlra a felhőben.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Hivatkozás fájlra',
     'attachments_link_url_hint' => 'Weboldal vagy fájl webcíme',
     'attach' => 'Csatolás',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Fájl szerkesztése',
     'attachments_edit_file_name' => 'Fájl neve',
     'attachments_edit_drop_upload' => 'Feltöltés és felülírás ejtéssel vagy kattintással',
index 668e57783a0a8697581dd1b4e02a144ba07191a8..64229a19f3d3283025093b3c2a159890c82bcb21 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'A fáj feltöltése időtúllépést okozott.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Oldal eltárás csatolmány frissítése közben',
     'attachment_not_found' => 'Csatolmány nem található',
 
     // Pages
index f0c59da4bad3efd21307ea18e10a2fab273571b9..82485e104c14c3a89b8c6688eac773ac1c2f812c 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Az email kézbesítés működőképesnek tűnik!',
     'maint_send_test_email_mail_text' => 'Gratulálunk! Mivel ez az email figyelmeztetés megérkezett az email beállítások megfelelőek.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Szerepkörök',
     'role_user_roles' => 'Felhasználói szerepkörök',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Hozzáférés a rendszer API-hoz',
     'role_manage_settings' => 'Alkalmazás beállításainak kezelése',
     'role_asset' => 'Eszköz jogosultságok',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Ezek a jogosultság vezérlik a alapértelmezés szerinti hozzáférést a rendszerben található eszközökhöz. A könyvek, fejezetek és oldalak jogosultságai felülírják ezeket a jogosultságokat.',
     'role_asset_admins' => 'Az adminisztrátorok automatikusan hozzáférést kapnak minden tartalomhoz, de ezek a beállítások megjeleníthetnek vagy elrejthetnek felhasználói felület beállításokat.',
     'role_all' => 'Összes',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Lejárati dátum',
     'user_api_token_expiry_desc' => 'Dátum megadása ameddig a vezérjel érvényes. Ez után a dátum után az ezzel a vezérjellel történő kérések nem fognak működni. Üresen hagyva a lejárati idő 100 évre lesz beállítva.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API vezérjel sikeresen létrehozva',
     'user_api_token_update_success' => 'API vezérjel sikeresen frissítve',
     'user_api_token' => 'API vezérjel',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 8e8ff8f0780c3e35b60ce0ae0fe025905308af9b..a1c4b704831450ac3f61ce34cf13674e1c5b723f 100755 (executable)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Reimposta Password',
     'reset_password_send_instructions' => 'Inserisci il tuo indirizzo sotto e ti verrà inviata una mail contenente un link per resettare la tua password.',
     'reset_password_send_button' => 'Invia Link Reset',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Un link di reset della password verrà inviato a :email se la mail verrà trovata nel sistema.',
     'reset_password_success' => 'La tua password è stata resettata correttamente.',
     'email_reset_subject' => 'Reimposta la password di :appName',
     'email_reset_text' => 'Stai ricevendo questa mail perché abbiamo ricevuto una richiesta di reset della password per il tuo account.',
index 73b4cad54276b06971feb38ce98aafa8bbd0e931..9676962bdc7f94c5f04b03a1a82e7b4b522da686 100755 (executable)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copia',
     'reply' => 'Rispondi',
     'delete' => 'Elimina',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Cerca',
     'search_clear' => 'Pulisci Ricerca',
     'reset' => 'Azzera',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => 'Menu del profilo',
     'view_profile' => 'Visualizza Profilo',
     'edit_profile' => 'Modifica Profilo',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Modalità Scura',
+    'light_mode' => 'Modalità Chiara',
 
     // Layout tabs
     'tab_info' => 'Info',
index 360409646e3040033dd88acad388bd8a5870604d..aa1c8f4a68fea41b80bb5b590ec11ba72944b226 100755 (executable)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Carica Altre',
     'image_image_name' => 'Nome Immagine',
     'image_delete_used' => 'Questa immagine è usata nelle pagine elencate.',
-    'image_delete_confirm' => 'Clicca elimina nuovamente per confermare.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Seleziona Immagine',
     'image_dropzone' => 'Rilascia immagini o clicca qui per caricarle',
     'images_deleted' => 'Immagini Eliminate',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Modifica Codice',
     'code_language' => 'Linguaggio Codice',
     'code_content' => 'Contenuto Codice',
+    'code_session_history' => 'Cronologia Sessione',
     'code_save' => 'Salva Codice',
 ];
index 9f6acd133950a644808cb668c5d20a8b0956a7b1..ebc1976503b09441900acec94c211d55af58dd53 100755 (executable)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Nessuna pagina corrisponde alla ricerca',
     'search_for_term' => 'Ricerca per :term',
     'search_more' => 'Più Risultati',
-    'search_filters' => 'Filtri Ricerca',
+    'search_advanced' => 'Ricerca Avanzata',
+    'search_terms' => 'Termini Ricerca',
     'search_content_type' => 'Tipo di Contenuto',
     'search_exact_matches' => 'Corrispondenza Esatta',
     'search_tags' => 'Ricerche Tag',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Carica File',
     'attachments_link' => 'Allega Link',
     'attachments_set_link' => 'Imposta Link',
-    'attachments_delete_confirm' => 'Clicca elimina nuovamente per confermare l\'eliminazione di questo allegato.',
+    'attachments_delete' => 'Sei sicuro di voler eliminare questo allegato?',
     'attachments_dropzone' => 'Rilascia file o clicca qui per allegare un file',
     'attachments_no_files' => 'Nessun file è stato caricato',
     'attachments_explain_link' => 'Puoi allegare un link se preferisci non caricare un file. Questo può essere un link a un\'altra pagina o a un file nel cloud.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link al file',
     'attachments_link_url_hint' => 'Url del sito o del file',
     'attach' => 'Allega',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Modifica File',
     'attachments_edit_file_name' => 'Nome File',
     'attachments_edit_drop_upload' => 'Rilascia file o clicca qui per caricare e sovrascrivere',
index ca605cde0c9c71c90dd76f9be2ee12ec2fa31431..3e48ad762a942488b3c1d8bf99269fa2e3fb092a 100755 (executable)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Il caricamento del file è andato in timeout.',
 
     // Attachments
-    'attachment_page_mismatch' => 'La pagina non è corrisposta durante l\'aggiornamento dell\'allegato',
     'attachment_not_found' => 'Allegato non trovato',
 
     // Pages
index 34342d3c73f821eb1510f6cc6ab4e500f2d37079..b49281851b4bf07530c6bcf52b422bac3e13e6c0 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'L\'invio delle email sembra funzionare!',
     'maint_send_test_email_mail_text' => 'Congratulazioni! Siccome hai ricevuto questa notifica email, le tue impostazioni sembrano essere configurate correttamente.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Ruoli',
     'role_user_roles' => 'Ruoli Utente',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Gestire impostazioni app',
     'role_asset' => 'Permessi Entità',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Questi permessi controllano l\'accesso di default alle entità. I permessi nei Libri, Capitoli e Pagine sovrascriveranno questi.',
     'role_asset_admins' => 'Gli amministratori hanno automaticamente accesso a tutti i contenuti ma queste opzioni possono mostrare o nascondere le opzioni della UI.',
     'role_all' => 'Tutti',
@@ -154,30 +169,30 @@ return [
     'users_social_disconnected' => 'L\'account :socialAccount è stato disconnesso correttamente dal tuo profilo.',
     'users_api_tokens' => 'API Tokens',
     'users_api_tokens_none' => 'No API tokens have been created for this user',
-    'users_api_tokens_create' => 'Create Token',
-    'users_api_tokens_expires' => 'Expires',
+    'users_api_tokens_create' => 'Crea Token',
+    'users_api_tokens_expires' => 'Scade',
     'users_api_tokens_docs' => 'API Documentation',
 
     // API Tokens
-    'user_api_token_create' => 'Create API Token',
+    'user_api_token_create' => 'Crea Token API',
     'user_api_token_name' => 'Nome',
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Data di scadenza',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
-    'user_api_token' => 'API Token',
+    'user_api_token' => 'Token API',
     'user_api_token_id' => 'Token ID',
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
-    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_created' => 'Token Aggiornato :timeAgo',
+    'user_api_token_updated' => 'Token Aggiornato :timeAgo',
+    'user_api_token_delete' => 'Elimina Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
-    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
-    'user_api_token_delete_success' => 'API token successfully deleted',
+    'user_api_token_delete_confirm' => 'Sei sicuri di voler eliminare questo token API?',
+    'user_api_token_delete_success' => 'Token API eliminato correttamente',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Danese',
         'de' => 'Deutsch (Sie)',
index de1ca1ac6c9cdea7475e8acc830ca5ed1856635c..1cc60eb540e305e2920a8adc4c32a5cb1a37e3ee 100644 (file)
@@ -36,13 +36,13 @@ return [
     'book_sort_notification'      => '並び順を変更しました',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => '本棚を作成:',
+    'bookshelf_create_notification'    => '本棚を作成しました',
+    'bookshelf_update'                 => '本棚を更新:',
+    'bookshelf_update_notification'    => '本棚を更新しました',
+    'bookshelf_delete'                 => 'ブックが削除されました。',
+    'bookshelf_delete_notification'    => '本棚を削除しました',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => 'コメントする',
 ];
index a700ffb95a2e50329eab8e4761c2a294a5b2b564..6163a5fc1f739b171b332661d7112fb282a13d4e 100644 (file)
@@ -26,8 +26,8 @@ return [
     'remember_me' => 'ログイン情報を保存する',
     'ldap_email_hint' => 'このアカウントで使用するEメールアドレスを入力してください。',
     'create_account' => 'アカウント作成',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'すでにアカウントをお持ちですか?',
+    'dont_have_account' => '初めての登録ですか?',
     'social_login' => 'SNSログイン',
     'social_registration' => 'SNS登録',
     'social_registration_text' => '他のサービスで登録 / ログインする',
index 9611e232b532a487e5cc5951d940177e6831912f..7932dc7135bbf9fc5192b8e7df0405a67503d1dd 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copy',
     'reply' => '返信',
     'delete' => '削除',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => '検索',
     'search_clear' => '検索をクリア',
     'reset' => 'リセット',
index 7cc560b43cfcb692cae22a1824f9bccd25f9838e..c4e44433787858f367e72133eba3dfab288b49e2 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'さらに読み込む',
     'image_image_name' => '画像名',
     'image_delete_used' => 'この画像は以下のページで利用されています。',
-    'image_delete_confirm' => '削除してもよろしければ、再度ボタンを押して下さい。',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => '画像を選択',
     'image_dropzone' => '画像をドロップするか、クリックしてアップロード',
     'images_deleted' => '画像を削除しました',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'コードを編集する',
     'code_language' => 'プログラミング言語の選択',
     'code_content' => 'プログラム内容',
+    'code_session_history' => 'セッション履歴',
     'code_save' => 'プログラムを保存',
 ];
index 4f1a855ff78a2a1414ce4082490716049f488edd..2e5bfda75178b02e6bb0609a32eb5864b26705ff 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'ページが見つかりませんでした。',
     'search_for_term' => ':term の検索結果',
     'search_more' => 'さらに表示',
-    'search_filters' => '検索フィルタ',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => '種類',
     'search_exact_matches' => '完全一致',
     'search_tags' => 'タグ検索',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'アップロード',
     'attachments_link' => 'リンクを添付',
     'attachments_set_link' => 'リンクを設定',
-    'attachments_delete_confirm' => 'もう一度クリックし、削除を確認してください。',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'ファイルをドロップするか、クリックして選択',
     'attachments_no_files' => 'ファイルはアップロードされていません',
     'attachments_explain_link' => 'ファイルをアップロードしたくない場合、他のページやクラウド上のファイルへのリンクを添付できます。',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'ファイルURL',
     'attachments_link_url_hint' => 'WebサイトまたはファイルへのURL',
     'attach' => '添付',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'ファイルを編集',
     'attachments_edit_file_name' => 'ファイル名',
     'attachments_edit_drop_upload' => 'ファイルをドロップするか、クリックしてアップロード',
index ace2f77bd3d6d9947de0a707ee4b525ed35eccaa..983e07a3a524aed553fb875fa070aed5a466b0d1 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'ファイルのアップロードがタイムアウトしました。',
 
     // Attachments
-    'attachment_page_mismatch' => '添付を更新するページが一致しません',
     'attachment_not_found' => '添付ファイルが見つかりません。',
 
     // Pages
index c7a9773e308735c48417460609209c77c5dafba7..303ff57bfa83c73cc1dccffe678844517cfe6e2a 100644 (file)
@@ -17,7 +17,7 @@ return [
     'app_name' => 'アプリケーション名',
     'app_name_desc' => 'この名前はヘッダーやEメール内で表示されます。',
     'app_name_header' => 'ヘッダーにアプリケーション名を表示する',
-    'app_public_access' => 'Public Access',
+    'app_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',
@@ -36,13 +36,13 @@ return [
     'app_primary_color_desc' => '16進数カラーコードで入力します。空にした場合、デフォルトの色にリセットされます。',
     '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_homepage_select' => 'ページを選択',
     'app_disable_comments' => 'コメントを無効にする',
-    'app_disable_comments_toggle' => 'Disable comments',
+    'app_disable_comments_toggle' => 'コメントを無効にする',
     'app_disable_comments_desc' => 'アプリケーション内のすべてのページのコメントを無効にします。既存のコメントは表示されません。',
 
     // Color settings
-    'content_colors' => 'Content Colors',
+    'content_colors' => 'コンテンツの色',
     'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
     'bookshelf_color' => 'Shelf Color',
     'book_color' => 'Book Color',
@@ -65,22 +65,36 @@ return [
     'reg_confirm_restrict_domain_placeholder' => '制限しない',
 
     // Maintenance settings
-    'maint' => 'Maintenance',
+    'maint' => 'メンテナンス',
     'maint_image_cleanup' => 'Cleanup Images',
     'maint_image_cleanup_desc' => "Scans page & revision content to check which images and drawings are currently in use and which images are redundant. Ensure you create a full database and image backup before running this.",
     'maint_image_cleanup_ignore_revisions' => 'Ignore images in revisions',
-    'maint_image_cleanup_run' => 'Run Cleanup',
+    'maint_image_cleanup_run' => 'クリーンアップを実行',
     'maint_image_cleanup_warning' => ':count potentially unused images were found. Are you sure you want to delete these images?',
     'maint_image_cleanup_success' => ':count potentially unused images found and deleted!',
     'maint_image_cleanup_nothing_found' => 'No unused images found, Nothing deleted!',
-    'maint_send_test_email' => 'Send a Test Email',
+    'maint_send_test_email' => 'テストメールを送信',
     'maint_send_test_email_desc' => 'This sends a test email to your email address specified in your profile.',
     'maint_send_test_email_run' => 'Send test email',
     'maint_send_test_email_success' => 'Email sent to :address',
-    'maint_send_test_email_mail_subject' => 'Test Email',
+    'maint_send_test_email_mail_subject' => 'テストメール',
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => '役割',
     'role_user_roles' => '役割',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'アプリケーション設定の管理',
     'role_asset' => 'アセット権限',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => '各アセットに対するデフォルトの権限を設定します。ここで設定した権限が優先されます。',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => '全て',
@@ -126,7 +141,7 @@ return [
     'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
     'users_role' => 'ユーザ役割',
     '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' => 'ユーザー パスワード',
     'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
     'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
     'users_send_invite_option' => 'Send user invite email',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index c3fedeb45ac8ec0c1f2698c8a4495629e065b153..a56316ef82db2f3cd6d49d02949ec7f84122ac1d 100644 (file)
@@ -10,27 +10,27 @@ return [
     'page_create_notification'    => '문서 만듦',
     'page_update'                 => '문서 수정',
     'page_update_notification'    => '문서 수정함',
-    'page_delete'                 => '문서 지우기',
+    'page_delete'                 => '삭제 된 페이지',
     'page_delete_notification'    => '문서 지움',
     'page_restore'                => '문서 복원',
     'page_restore_notification'   => '문서 복원함',
-    'page_move'                   => '문ì\84\9c ì\98®ê¸°ê¸°',
+    'page_move'                   => '문ì\84\9c ì\9d´ë\8f\84á\86¼ë\90¨',
 
     // Chapters
     'chapter_create'              => '챕터 만들기',
     'chapter_create_notification' => '챕터 만듦',
     'chapter_update'              => '챕터 바꾸기',
     'chapter_update_notification' => '챕터 바꿈',
-    'chapter_delete'              => 'ì±\95í\84° ì§\80ì\9a°ê¸°',
+    'chapter_delete'              => 'ì\82­ì \9cë\90\9c ì±\95í\84°',
     'chapter_delete_notification' => '챕터 지움',
-    'chapter_move'                => 'ì±\95í\84° ì\98®ê¸°ê¸°',
+    'chapter_move'                => 'ì±\95í\84° ì\9d´ë\8f\99ë\90\9c',
 
     // Books
     'book_create'                 => '책자 만들기',
     'book_create_notification'    => '책자 만듦',
     'book_update'                 => '책자 바꾸기',
     'book_update_notification'    => '책자 바꿈',
-    'book_delete'                 => 'ì±\85ì\9e\90 ì§\80ì\9a°ê¸°',
+    'book_delete'                 => 'ì\82­ì \9c ë\90\9c ì±\85ì\9e\90',
     'book_delete_notification'    => '책자 지움',
     'book_sort'                   => '책자 정렬',
     'book_sort_notification'      => '책자 정렬함',
@@ -40,7 +40,7 @@ return [
     'bookshelf_create_notification'    => '서가 만듦',
     'bookshelf_update'                 => '서가 바꾸기',
     'bookshelf_update_notification'    => '서가 바꿈',
-    'bookshelf_delete'                 => 'ì\84\9cê°\80 ì§\80ì\9a°ê¸°',
+    'bookshelf_delete'                 => 'ì\82­ì \9cë\90\9c ì\84\9cê°\80',
     'bookshelf_delete_notification'    => '서가 지움',
 
     // Other
index 9c4d98bcb01be1d3b40624e686e63f39ec7d1f31..0bff8724c955ed3f840e29c17cb02108e0d3bd31 100644 (file)
@@ -6,8 +6,8 @@
  */
 return [
 
-    'failed' => '가입하지 않았거나 비밀번호가 틀립니다.',
-    'throttle' => '여러 번 실패했습니다. :seconds초 후에 다시 시도하세요.',
+    'failed' => '자격 증명이 기록과 일치하지 않습니다.',
+    'throttle' => '로그인 시도가 너무 많습니다. :seconds초 후에 다시 시도하세요.',
 
     // Login & Register
     'sign_up' => '가입',
@@ -43,7 +43,7 @@ return [
     'reset_password' => '비밀번호 바꾸기',
     'reset_password_send_instructions' => '메일 주소를 입력하세요. 이 주소로 해당 과정을 위한 링크를 보낼 것입니다.',
     'reset_password_send_button' => '메일 보내기',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => '시스템에서 이메일 주소가 발견되면, 암호 재설정 링크가 :email로 전송된다.',
     'reset_password_success' => '비밀번호를 바꿨습니다.',
     'email_reset_subject' => ':appName 비밀번호 바꾸기',
     'email_reset_text' => '비밀번호를 바꿉니다.',
@@ -70,8 +70,8 @@ return [
     'user_invite_email_greeting' => ':appName에서 가입한 기록이 있습니다.',
     'user_invite_email_text' => '다음 버튼을 눌러 확인하세요:',
     'user_invite_email_action' => '비밀번호 설정',
-    'user_invite_page_welcome' => ':appName로 접속했습니다.',
+    'user_invite_page_welcome' => ':appName에 오신 것을 환영합니다!',
     'user_invite_page_text' => ':appName에 로그인할 때 입력할 비밀번호를 설정하세요.',
     'user_invite_page_confirm_button' => '비밀번호 확인',
-    'user_invite_success' => '이제 :appName에 접근할 수 있습니다.'
+    'user_invite_success' => 'ì\95\94í\98¸ê°\80 ì\84¤ì \95ë\90\98ì\97\88ê³ , ì\9d´ì \9c :appNameì\97\90 ì \91ê·¼í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.'
 ];
\ No newline at end of file
index 67cc87bd23546e8ed94991fc6d239bd50a1cb386..934c3f607268715593603fd0c372c9856b3b75a0 100644 (file)
@@ -29,12 +29,13 @@ return [
     'update' => '바꾸기',
     'edit' => '수정',
     'sort' => '정렬',
-    'move' => 'ì\98®ê¸°ê¸°',
+    'move' => 'ì\9d´ë\8f\99',
     'copy' => '복사',
     'reply' => '답글',
-    'delete' => '지우기',
+    'delete' => '삭제',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => '검색',
-    'search_clear' => '기ë¡\9d 지우기',
+    'search_clear' => 'ê²\80ì\83\89 지우기',
     'reset' => '리셋',
     'remove' => '제거',
     'add' => '추가',
index 397d0d1879c12168090f7129c21b4b761755fd27..1eadfccfa891745c122e75ff7cca3bfcd0bfbe1a 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => '더 로드하기',
     'image_image_name' => '이미지 이름',
     'image_delete_used' => '이 이미지는 다음 문서들이 쓰고 있습니다.',
-    'image_delete_confirm' => '이 이미지를 지울 건가요?',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => '이미지 선택',
     'image_dropzone' => '여기에 이미지를 드롭하거나 여기를 클릭하세요. 이미지를 올릴 수 있습니다.',
     'images_deleted' => '이미지 삭제함',
@@ -29,5 +29,6 @@ return [
     'code_editor' => '코드 수정',
     'code_language' => '언어',
     'code_content' => '내용',
+    'code_session_history' => 'Session History',
     'code_save' => '저장',
 ];
index a166cda40557cf05b9d55eba5b1a85d2aeef6ec6..a238b9a8ca08c5a9282524a685a67d79ae6d3751 100644 (file)
@@ -24,7 +24,7 @@ return [
     'meta_updated_name' => '수정함 :timeLength, :user',
     'entity_select' => '항목 선택',
     'images' => '이미지',
-    'my_recent_drafts' => '쓰다 만 문서',
+    'my_recent_drafts' => '내 최근의 초안 문서',
     'my_recently_viewed' => '내가 읽은 문서',
     'no_pages_viewed' => '문서 없음',
     'no_pages_recently_created' => '문서 없음',
@@ -43,11 +43,12 @@ return [
     // Search
     'search_results' => '검색 결과',
     'search_total_results_found' => ':count개|총 :count개',
-    'search_clear' => '기ë¡\9d 지우기',
+    'search_clear' => 'ê²\80ì\83\89 지우기',
     'search_no_pages' => '결과 없음',
     'search_for_term' => ':term 검색',
     'search_more' => '더 많은 결과',
-    'search_filters' => '고급 검색',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => '형식',
     'search_exact_matches' => '정확히 일치',
     'search_tags' => '꼬리표 일치',
@@ -85,8 +86,8 @@ return [
     'shelves_edit_and_assign' => '서가 바꾸기로 책자를 추가하세요.',
     'shelves_edit_named' => ':name 바꾸기',
     'shelves_edit' => '서가 바꾸기',
-    'shelves_delete' => 'ì\84\9cê°\80 ì§\80ì\9a°기',
-    'shelves_delete_named' => ':name ì§\80ì\9a°기',
+    'shelves_delete' => 'ì\84\9cê°\80 ì\82­ì \9cí\95\98기',
+    'shelves_delete_named' => ':name ì\82­ì \9cí\95\98기',
     'shelves_delete_explain' => ":name을 지웁니다. 책자는 지우지 않습니다.",
     'shelves_delete_confirmation' => '이 서가를 지울 건가요?',
     'shelves_permissions' => '서가 권한',
@@ -109,7 +110,7 @@ return [
     'books_popular_empty' => '많이 읽은 책자 목록',
     'books_new_empty' => '새로운 책자 목록',
     'books_create' => '책자 만들기',
-    'books_delete' => 'ì±\85ì\9e\90 ì§\80ì\9a°기',
+    'books_delete' => 'ì±\85ì\9e\90 ì\82­ì \9cí\95\98기',
     'books_delete_named' => ':bookName(을)를 지웁니다.',
     'books_delete_explain' => ':bookName에 있는 모든 챕터와 문서도 지웁니다.',
     'books_delete_confirmation' => '이 책자를 지울 건가요?',
@@ -143,15 +144,15 @@ return [
     'chapters_popular' => '많이 읽은 챕터',
     'chapters_new' => '새로운 챕터',
     'chapters_create' => '챕터 만들기',
-    'chapters_delete' => 'ì±\95í\84° ì§\80ì\9a°기',
+    'chapters_delete' => 'ì±\95í\84° ì\82­ì \9cí\95\98기',
     'chapters_delete_named' => ':chapterName(을)를 지웁니다.',
     'chapters_delete_explain' => ':chapterName에 있는 모든 문서는 챕터에서 벗어날 뿐 지우지 않습니다.',
     'chapters_delete_confirm' => '이 챕터를 지울 건가요?',
     'chapters_edit' => '챕터 바꾸기',
     'chapters_edit_named' => ':chapterName 바꾸기',
     'chapters_save' => '저장',
-    'chapters_move' => 'ì±\95í\84° ì\98®ê¸°기',
-    'chapters_move_named' => ':chapterName ì\98®ê¸°기',
+    'chapters_move' => 'ì±\95í\84° ì\9d´ë\8f\99í\95\98기',
+    'chapters_move_named' => ':chapterName ì\9d´ë\8f\99í\95\98기',
     'chapter_move_success' => ':bookName(으)로 옮김',
     'chapters_permissions' => '챕터 권한',
     'chapters_empty' => '이 챕터에 문서가 없습니다.',
@@ -167,22 +168,22 @@ return [
     'pages_new' => '새로운 문서',
     'pages_attachments' => '첨부',
     'pages_navigation' => '목차',
-    'pages_delete' => '문ì\84\9c ì§\80ì\9a°기',
-    'pages_delete_named' => ':pageName ì§\80ì\9a°기',
-    'pages_delete_draft_named' => ':pageName ì§\80ì\9a°기',
-    'pages_delete_draft' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c ì§\80ì\9a°기',
+    'pages_delete' => '문ì\84\9c ì\82­ì \9cí\95\98기',
+    'pages_delete_named' => ':pageName ì\82­ì \9cí\95\98기',
+    'pages_delete_draft_named' => ':pageName ì´\88ì\95\88 ë¬¸ì\84\9c ì\82­ì \9cí\95\98기',
+    'pages_delete_draft' => 'ì´\88ì\95\88 ë¬¸ì\84\9c ì\82­ì \9cí\95\98기',
     'pages_delete_success' => '문서 지움',
-    'pages_delete_draft_success' => 'ì\93°ë\8b¤ ë§\8c 문서 지움',
+    'pages_delete_draft_success' => 'ì´\88ì\95\88 문서 지움',
     'pages_delete_confirm' => '이 문서를 지울 건가요?',
-    'pages_delete_draft_confirm' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c를 ì§\80ì\9a¸ 건가요?',
+    'pages_delete_draft_confirm' => 'ì´\88ì\95\88 ë¬¸ì\84\9c를 ì\82­ì \9cí\95  건가요?',
     'pages_editing_named' => ':pageName 수정',
-    'pages_edit_draft_options' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c ì\84 í\83\9d',
-    'pages_edit_save_draft' => '보관',
-    'pages_edit_draft' => 'ì\93°ë\8b¤ ë§\8c 문서 수정',
-    'pages_editing_draft' => 'ì\93°ë\8b¤ ë§\8c 문서 수정',
+    'pages_edit_draft_options' => 'ì´\88ì\95\88 ë¬¸ì\84\9c ì\98µì\85\98',
+    'pages_edit_save_draft' => '초안으로 저장',
+    'pages_edit_draft' => 'ì´\88ì\95\88 문서 수정',
+    'pages_editing_draft' => 'ì´\88ì\95\88 문서 수정',
     'pages_editing_page' => '문서 수정',
     'pages_edit_draft_save_at' => '보관함: ',
-    'pages_edit_delete_draft' => '삭제',
+    'pages_edit_delete_draft' => 'ì´\88ì\95\88 ì\82­ì \9c',
     'pages_edit_discard_draft' => '폐기',
     'pages_edit_set_changelog' => '수정본 설명',
     'pages_edit_enter_changelog_desc' => '수정본 설명',
@@ -196,7 +197,7 @@ return [
     'pages_md_insert_link' => '내부 링크',
     'pages_md_insert_drawing' => '드로잉 추가',
     'pages_not_in_chapter' => '챕터에 있는 문서가 아닙니다.',
-    'pages_move' => '문ì\84\9c ì\98®ê¸°기',
+    'pages_move' => '문ì\84\9c ì\9d´ë\8f\99í\95\98기',
     'pages_move_success' => ':parentName(으)로 옮김',
     'pages_copy' => '문서 복제',
     'pages_copy_desination' => '복제할 위치',
@@ -223,8 +224,8 @@ return [
     'pages_permissions_active' => '문서 권한 허용함',
     'pages_initial_revision' => '처음 판본',
     'pages_initial_name' => '제목 없음',
-    'pages_editing_draft_notification' => ':timeDiffì\97\90 ì\93°ë\8b¤ ë§\8c 문서입니다.',
-    'pages_draft_edited_notification' => 'ìµ\9cê·¼ì\97\90 ì\88\98ì \95í\95\9c ë¬¸ì\84\9cì\9d´ê¸° ë\95\8c문ì\97\90 ì\93°ë\8b¤ ë§\8c 문서를 폐기하는 편이 좋습니다.',
+    'pages_editing_draft_notification' => ':timeDiffì\97\90 ì´\88ì\95\88 문서입니다.',
+    'pages_draft_edited_notification' => 'ìµ\9cê·¼ì\97\90 ì\88\98ì \95í\95\9c ë¬¸ì\84\9cì\9d´ê¸° ë\95\8c문ì\97\90 ì´\88ì\95\88 문서를 폐기하는 편이 좋습니다.',
     'pages_draft_edit_active' => [
         'start_a' => ':count명이 이 문서를 수정하고 있습니다.',
         'start_b' => ':userName이 이 문서를 수정하고 있습니다.',
@@ -232,7 +233,7 @@ return [
         'time_b' => '(:minCount분 전)',
         'message' => ':start :time. 다른 사용자의 수정본을 덮어쓰지 않도록 주의하세요.',
     ],
-    'pages_draft_discarded' => 'ì\93°ë\8b¤ ë§\8c 문서를 지웠습니다. 에디터에 현재 판본이 나타납니다.',
+    'pages_draft_discarded' => 'ì´\88ì\95\88 문서를 지웠습니다. 에디터에 현재 판본이 나타납니다.',
     'pages_specific' => '특정한 문서',
     'pages_is_template' => '템플릿',
 
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => '파일 올리기',
     'attachments_link' => '링크로 첨부',
     'attachments_set_link' => '링크 설정',
-    'attachments_delete_confirm' => '삭제하려면 버튼을 한 번 더 클릭하세요.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => '여기에 파일을 드롭하거나 여기를 클릭하세요.',
     'attachments_no_files' => '올린 파일 없음',
     'attachments_explain_link' => '파일을 올리지 않고 링크로 첨부할 수 있습니다.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => '파일로 링크',
     'attachments_link_url_hint' => '파일 주소',
     'attach' => '파일 첨부',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => '파일 수정',
     'attachments_edit_file_name' => '파일 이름',
     'attachments_edit_drop_upload' => '여기에 파일을 드롭하거나 여기를 클릭하세요. 파일을 올리거나 덮어쓸 수 있습니다.',
index a9e917e912aa453fa85e08ec3b3a8f1baa6af7c6..093288c83a7f59f52c3db9dceec1475be5320bc4 100644 (file)
@@ -13,7 +13,7 @@ return [
     'email_already_confirmed' => '확인이 끝난 메일 주소입니다. 로그인하세요.',
     'email_confirmation_invalid' => '이 링크는 더 이상 유효하지 않습니다. 다시 가입하세요.',
     'email_confirmation_expired' => '이 링크는 더 이상 유효하지 않습니다. 메일을 다시 보냈습니다.',
-    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'email_confirmation_awaiting' => '사용 중인 계정의 이메일 주소를 확인해 주어야 합니다.',
     'ldap_fail_anonymous' => '익명 정보로 LDAP 서버에 접근할 수 없습니다.',
     'ldap_fail_authed' => '이 정보로 LDAP 서버에 접근할 수 없습니다.',
     'ldap_extension_not_installed' => 'PHP에 LDAP 확장 도구를 설치하세요.',
@@ -46,11 +46,10 @@ return [
     'file_upload_timeout' => '파일을 올리는 데 걸리는 시간이 서버에서 허용하는 수치를 넘습니다.',
 
     // Attachments
-    'attachment_page_mismatch' => '올리는 위치와 현재 문서가 다릅니다.',
     'attachment_not_found' => '첨부 파일이 없습니다.',
 
     // Pages
-    'page_draft_autosave_fail' => 'ì\93°ë\8b¤ ë§\8c 문서를 유실했습니다. 인터넷 연결 상태를 확인하세요.',
+    'page_draft_autosave_fail' => 'ì´\88ì\95\88 문서를 유실했습니다. 인터넷 연결 상태를 확인하세요.',
     'page_custom_home_deletion' => '처음 페이지는 지울 수 없습니다.',
 
     // Entities
@@ -61,7 +60,7 @@ return [
     'chapter_not_found' => '챕터가 없습니다.',
     'selected_book_not_found' => '고른 책자가 없습니다.',
     'selected_book_chapter_not_found' => '고른 책자나 챕터가 없습니다.',
-    'guests_cannot_save_drafts' => 'Guestë\8a\94 ì\93°ë\8b¤ ë§\8c 문서를 보관할 수 없습니다.',
+    'guests_cannot_save_drafts' => 'Guestë\8a\94 ì´\88ì\95\88 문서를 보관할 수 없습니다.',
 
     // Users
     'users_cannot_delete_only_admin' => 'Admin을 삭제할 수 없습니다.',
@@ -75,7 +74,7 @@ return [
 
     // Comments
     'comment_list' => '댓글을 가져오다 문제가 생겼습니다.',
-    'cannot_add_comment_to_draft' => 'ì\93°ë\8b¤ ë§\8c 문서에 댓글을 달 수 없습니다.',
+    'cannot_add_comment_to_draft' => 'ì´\88ì\95\88 문서에 댓글을 달 수 없습니다.',
     'comment_add' => '댓글을 등록하다 문제가 생겼습니다.',
     'comment_delete' => '댓글을 지우다 문제가 생겼습니다.',
     'empty_comment' => '빈 댓글은 등록할 수 없습니다.',
@@ -83,21 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => '404 Not Found',
     'sorry_page_not_found' => '문서를 못 찾았습니다.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => '이 페이지가 존재하기를 기대했다면, 볼 수 있는 권한이 없을 수 있다.',
     'return_home' => '처음으로 돌아가기',
     'error_occurred' => '문제가 생겼습니다.',
     'app_down' => ':appName에 문제가 있는 것 같습니다',
     'back_soon' => '곧 되돌아갑니다.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
-    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
-    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
-    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
-    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_no_authorization_found' => '요청에서 인증 토큰을 찾을 수 없다.',
+    'api_bad_authorization_format' => '요청에서 인증 토큰을 찾았지만, 형식이 잘못된 것 같다.',
+    'api_user_token_not_found' => '제공된 인증 토큰과 일치하는 API 토큰을 찾을 수 없다.',
+    'api_incorrect_token_secret' => '사용한 API 토큰에 대해 제공한 시크릿이 맞지 않는다.',
+    'api_user_no_api_permission' => '사용한 API 토큰의 소유자가, API 호출을 할 수 있는 권한이 없다.',
+    'api_user_token_expired' => '사용된 인증 토큰이 만료되었다.',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => '테스트 이메일 발송할 때 발생한 오류:',
 
 ];
index 01633977136f940cab59a6e492ba221ddb8f4032..f93902aefd42bb257a14df002c8d989faae5e071 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => '여덟 글자를 넘어야 합니다.',
     'user' => "메일 주소를 가진 사용자가 없습니다.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => '비밀번호 재설정 토큰이 이 이메일 주소에 유효하지 않습니다.',
     'sent' => '메일을 보냈습니다.',
     'reset' => '비밀번호를 바꿨습니다.',
 
index e7cec851a253bb1242429d13b93ba13cc6c509b2..7c368543fec7c6f6568cba441c2113b7c26d09e4 100755 (executable)
@@ -48,7 +48,7 @@ return [
     'book_color' => '책 색상',
     'chapter_color' => '챕터 색상',
     'page_color' => '페이지 색상',
-    'page_draft_color' => '드래프트 페이지 색상',
+    'page_draft_color' => '초안 페이지 색상',
 
     // Registration Settings
     'reg_settings' => '가입',
@@ -56,7 +56,7 @@ return [
     'reg_enable_toggle' => '사이트 가입 허용',
     'reg_enable_desc' => '가입한 사용자는 단일한 권한을 가집니다.',
     'reg_default_role' => '가입한 사용자의 기본 권한',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_enable_external_warning' => '외부 LDAP 또는 SAML 인증이 활성화되어 있는 동안에는 위의 옵션이 무시된다. 사용 중인 외부 시스템에 대해 인증이 성공하면, 존재하지 않는 회원에 대한 사용자 계정이 자동으로 생성된다.',
     'reg_email_confirmation' => '메일 주소 확인',
     'reg_email_confirmation_toggle' => '주소 확인 요구',
     'reg_confirm_email_desc' => '도메인 차단을 쓰고 있으면 메일 주소를 확인해야 하고, 이 설정은 무시합니다.',
@@ -81,12 +81,26 @@ return [
     'maint_send_test_email_mail_greeting' => '이메일 전송이 성공하였습니다.',
     'maint_send_test_email_mail_text' => '축하합니다! 이 메일을 받음으로 이메일 설정이 정상적으로 되었음을 확인하였습니다.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => '권한',
     'role_user_roles' => '사용자 권한',
     'role_create' => '권한 만들기',
     'role_create_success' => '권한 만듦',
-    'role_delete' => 'ê¶\8cí\95\9c ì§\80ì\9a°ê¸°',
+    'role_delete' => 'ê¶\8cí\95\9c ì \9cê±°',
     'role_delete_confirm' => ':roleName(을)를 지웁니다.',
     'role_delete_users_assigned' => '이 권한을 가진 사용자 :userCount명에 할당할 권한을 고르세요.',
     'role_delete_no_migration' => "할당하지 않음",
@@ -106,6 +120,7 @@ return [
     'role_access_api' => '시스템 접근 API',
     'role_manage_settings' => '사이트 설정 관리',
     'role_asset' => '권한 항목',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => '책자, 챕터, 문서별 권한은 이 설정에 우선합니다.',
     'role_asset_admins' => 'Admin 권한은 어디든 접근할 수 있지만 이 설정은 사용자 인터페이스에서 해당 활동을 표시할지 결정합니다.',
     'role_all' => '모든 항목',
@@ -126,12 +141,12 @@ return [
     'users_details_desc_no_email' => '사용자 이름을 바꿉니다.',
     'users_role' => '사용자 권한',
     'users_role_desc' => '고른 권한 모두를 적용합니다.',
-    'users_password' => '비밀번호',
+    'users_password' => '사용자 비밀번호',
     'users_password_desc' => '여섯 글자를 넘어야 합니다.',
     'users_send_invite_text' => '비밀번호 설정을 권유하는 메일을 보내거나 내가 정할 수 있습니다.',
     'users_send_invite_option' => '메일 보내기',
     'users_external_auth_id' => 'LDAP 확인',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_external_auth_id_desc' => '외부 인증 시스템과 통신할 때 사용자와 연결시키는 데 사용되는 ID 입니다.',
     'users_password_warning' => '비밀번호를 바꿀 때만 쓰세요.',
     'users_system_public' => '계정 없는 모든 사용자에 할당한 사용자입니다. 이 사용자로 로그인할 수 없어요.',
     'users_delete' => '사용자 삭제',
@@ -153,7 +168,7 @@ return [
     'users_social_connected' => ':socialAccount(와)과 연결했습니다.',
     'users_social_disconnected' => ':socialAccount(와)과의 연결을 끊었습니다.',
     'users_api_tokens' => 'API 토큰',
-    'users_api_tokens_none' => 'No API tokens have been created for this user',
+    'users_api_tokens_none' => '이 사용자를 위해 생성된 API 토큰이 없습니다.',
     'users_api_tokens_create' => '토큰 만들기',
     'users_api_tokens_expires' => '만료',
     'users_api_tokens_docs' => 'API 설명서',
@@ -161,23 +176,23 @@ return [
     // API Tokens
     'user_api_token_create' => 'API 토큰 만들기',
     'user_api_token_name' => '제목',
-    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
+    'user_api_token_name_desc' => '토큰이 의도한 목적을 향후에 상기시키기 위해 알아보기 쉬운 이름을 지정한다.',
     'user_api_token_expiry' => '만료일',
-    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
-    'user_api_token_create_success' => 'API token successfully created',
-    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token_expiry_desc' => '이 토큰이 만료되는 날짜를 설정한다. 이 날짜가 지나면 이 토큰을 사용하여 만든 요청은 더 이상 작동하지 않는다. 이 칸을 공백으로 두면 100년 뒤가 만기가 된다.',
+    'user_api_token_create_secret_message' => '이 토큰을 만든 직후 "토큰 ID"와 "토큰 시크릿"이 생성되서 표시 된다. 시크릿은 한 번만 표시되므로 계속 진행하기 전에 안전하고 안심할 수 있는 곳에 값을 복사한다.',
+    'user_api_token_create_success' => 'API 토큰이 성공적으로 생성되었다.',
+    'user_api_token_update_success' => 'API 토큰이 성공적으로 갱신되었다.',
     'user_api_token' => 'API 토큰',
     'user_api_token_id' => '토큰 ID',
-    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
-    'user_api_token_secret' => 'Token Secret',
-    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_id_desc' => '이 토큰은 API 요청으로 제공되어야 하는 편집 불가능한 시스템이 생성한 식별자다.',
+    'user_api_token_secret' => '토큰 시크릿',
+    'user_api_token_secret_desc' => '이것은 API 요청시 제공되어야 할 이 토큰에 대한 시스템에서 생성된 시크릿이다. 이 값은 한 번만 표시되므로 안전하고 한심할 수 있는 곳에 이 값을 복사한다.',
+    'user_api_token_created' => ':timeAgo 전에 토큰이 생성되었다.',
+    'user_api_token_updated' => ':timeAgo 전에 토큰이 갱신되었다.',
     'user_api_token_delete' => '토큰 삭제',
-    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
-    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
-    'user_api_token_delete_success' => 'API token successfully deleted',
+    'user_api_token_delete_warning' => '이렇게 하면 시스템에서 \':tokenName\'이라는 이름을 가진 이 API 토큰이 완전히 삭제된다.',
+    'user_api_token_delete_confirm' => '이 API 토큰을 삭제하시겠습니까?',
+    'user_api_token_delete_success' => 'API 토큰이 성공적으로 삭제되었다.',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
@@ -192,7 +208,7 @@ return [
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
-        'he' => 'עברית',
+        'he' => '히브리어',
         'hu' => 'Magyar',
         'it' => 'Italian',
         'ja' => '日本語',
index f228fa6c47e0f2f9a9b3f92f7e02633b2481b5a9..7a2d633c65e61b2de0204f35f13dbf384351eb15 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopiëren',
     'reply' => 'Beantwoorden',
     'delete' => 'Verwijder',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Zoek',
     'search_clear' => 'Zoekopdracht wissen',
     'reset' => 'Resetten',
index 4083b57bebcc67ee04870aca22702892b67271d0..f40ac15b0f4c23342b6a245d7328b936ee4ce133 100644 (file)
@@ -5,7 +5,7 @@
 return [
 
     // Image Manager
-    'image_select' => 'Afbeelding selecteren',
+    'image_select' => 'Selecteer Afbeelding',
     'image_all' => 'Alles',
     'image_all_title' => 'Alle afbeeldingen weergeven',
     'image_book_title' => 'Afbeeldingen van dit boek weergeven',
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Meer Laden',
     'image_image_name' => 'Afbeeldingsnaam',
     'image_delete_used' => 'Deze afbeeldingen is op onderstaande pagina\'s in gebruik.',
-    'image_delete_confirm' => 'Klik opnieuw op verwijderen om de afbeelding echt te verwijderen.',
+    'image_delete_confirm_text' => 'Weet u zeker dat u deze afbeelding wilt verwijderen?',
     'image_select_image' => 'Kies Afbeelding',
     'image_dropzone' => 'Sleep afbeeldingen hier of klik hier om te uploaden',
     'images_deleted' => 'Verwijderde Afbeeldingen',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Code invoegen',
     'code_language' => 'Code taal',
     'code_content' => 'Code',
+    'code_session_history' => 'Zittingsgeschiedenis',
     'code_save' => 'Sla code op',
 ];
index a88be77119bd7f4e06f82a0759fc1351da6cec35..399d97f2567a1561e8a23ef8d00ed058ff4bb226 100644 (file)
@@ -30,9 +30,9 @@ return [
     'no_pages_recently_created' => 'Er zijn geen recent aangemaakte pagina\'s',
     'no_pages_recently_updated' => 'Er zijn geen recente wijzigingen',
     'export' => 'Exporteren',
-    'export_html' => 'Contained Web File',
-    'export_pdf' => 'PDF File',
-    'export_text' => 'Plain Text File',
+    'export_html' => 'Ingesloten Webbestand',
+    'export_pdf' => 'PDF Bestand',
+    'export_text' => 'Normaal Tekstbestand',
 
     // Permissions and restrictions
     'permissions' => 'Permissies',
@@ -47,22 +47,23 @@ return [
     'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
     'search_for_term' => 'Zoeken op :term',
     'search_more' => 'Meer resultaten',
-    'search_filters' => 'Zoek filters',
-    'search_content_type' => 'Content Type',
+    'search_advanced' => 'Uitgebreid zoeken',
+    'search_terms' => 'Zoektermen',
+    'search_content_type' => 'Inhoudstype',
     'search_exact_matches' => 'Exacte Matches',
     'search_tags' => 'Zoek tags',
-    'search_options' => 'Options',
+    'search_options' => 'Opties',
     'search_viewed_by_me' => 'Bekeken door mij',
     'search_not_viewed_by_me' => 'Niet bekeken door mij',
     'search_permissions_set' => 'Permissies gezet',
     'search_created_by_me' => 'Door mij gemaakt',
     'search_updated_by_me' => 'Door mij geupdate',
-    'search_date_options' => 'Date Options',
+    'search_date_options' => 'Datum Opties',
     'search_updated_before' => 'Geupdate voor',
     'search_updated_after' => 'Geupdate na',
-    'search_created_before' => 'Gecreeerd voor',
-    'search_created_after' => 'Gecreeerd na',
-    'search_set_date' => 'Zet datum',
+    'search_created_before' => 'Gecreëerd voor',
+    'search_created_after' => 'Gecreëerd na',
+    'search_set_date' => 'Stel datum in',
     'search_update' => 'Update zoekresultaten',
 
     // Shelves
@@ -74,7 +75,7 @@ return [
     'shelves_create' => 'Nieuwe Boekenplank Aanmaken',
     'shelves_popular' => 'Populaire Boekenplanken',
     'shelves_new' => 'Nieuwe Boekenplanken',
-    'shelves_new_action' => 'New Shelf',
+    'shelves_new_action' => 'Nieuwe Boekplank',
     'shelves_popular_empty' => 'De meest populaire boekenplanken worden hier weergegeven.',
     'shelves_new_empty' => 'De meest recent aangemaakt boekenplanken worden hier weergeven.',
     'shelves_save' => 'Boekenplanken Opslaan',
@@ -105,9 +106,9 @@ return [
     'books_popular' => 'Populaire Boeken',
     'books_recent' => 'Recente Boeken',
     'books_new' => 'Nieuwe Boeken',
-    'books_new_action' => 'New Book',
+    'books_new_action' => 'Nieuw Boek',
     'books_popular_empty' => 'De meest populaire boeken worden hier weergegeven.',
-    'books_new_empty' => 'The most recently created books will appear here.',
+    'books_new_empty' => 'De meest recent aangemaakte boeken verschijnen hier.',
     'books_create' => 'Nieuw Boek Aanmaken',
     'books_delete' => 'Boek Verwijderen',
     'books_delete_named' => 'Verwijder Boek :bookName',
@@ -128,11 +129,11 @@ return [
     'books_navigation' => 'Boek Navigatie',
     'books_sort' => 'Inhoud van het boek sorteren',
     'books_sort_named' => 'Sorteer Boek :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_name' => 'Sorteren op Naam',
+    'books_sort_created' => 'Sorteren op datum van aanmaken',
+    'books_sort_updated' => 'Sorteren op datum van bijgewerkt',
+    'books_sort_chapters_first' => 'Hoofdstukken eerst',
+    'books_sort_chapters_last' => 'Hoofdstukken Laatst',
     'books_sort_show_other' => 'Bekijk Andere Boeken',
     'books_sort_save' => 'Nieuwe Order Opslaan',
 
@@ -177,7 +178,7 @@ return [
     'pages_delete_confirm' => 'Weet je zeker dat je deze pagina wilt verwijderen?',
     'pages_delete_draft_confirm' => 'Weet je zeker dat je dit concept wilt verwijderen?',
     'pages_editing_named' => 'Pagina :pageName Bewerken',
-    'pages_edit_draft_options' => 'Draft Options',
+    'pages_edit_draft_options' => 'Concept Opties',
     'pages_edit_save_draft' => 'Concept opslaan',
     'pages_edit_draft' => 'Paginaconcept Bewerken',
     'pages_editing_draft' => 'Concept Bewerken',
@@ -195,13 +196,13 @@ return [
     'pages_md_preview' => 'Voorbeeld',
     'pages_md_insert_image' => 'Afbeelding Invoegen',
     'pages_md_insert_link' => 'Entity Link Invoegen',
-    'pages_md_insert_drawing' => 'Insert Drawing',
+    'pages_md_insert_drawing' => 'Tekening Toevoegen',
     'pages_not_in_chapter' => 'Deze pagina staat niet in een hoofdstuk',
     'pages_move' => 'Pagina Verplaatsten',
     'pages_move_success' => 'Pagina verplaatst naar ":parentName"',
-    'pages_copy' => 'Copy Page',
-    'pages_copy_desination' => 'Copy Destination',
-    'pages_copy_success' => 'Page successfully copied',
+    'pages_copy' => 'Pagina Kopiëren',
+    'pages_copy_desination' => 'Kopieerbestemming',
+    'pages_copy_success' => 'Pagina succesvol gekopieerd',
     'pages_permissions' => 'Pagina Permissies',
     'pages_permissions_success' => 'Pagina Permissies bijgwerkt',
     'pages_revision' => 'Revisie',
@@ -211,12 +212,12 @@ return [
     'pages_revisions_created_by' => 'Aangemaakt door',
     'pages_revisions_date' => 'Revisiedatum',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => 'Changelog',
+    'pages_revisions_numbered' => 'Revisie #:id',
+    'pages_revisions_numbered_changes' => 'Revisie #:id Wijzigingen',
+    'pages_revisions_changelog' => 'Wijzigingslogboek',
     'pages_revisions_changes' => 'Wijzigingen',
     'pages_revisions_current' => 'Huidige Versie',
-    'pages_revisions_preview' => 'Preview',
+    'pages_revisions_preview' => 'Voorbeeld',
     'pages_revisions_restore' => 'Herstellen',
     'pages_revisions_none' => 'Deze pagina heeft geen revisies',
     'pages_copy_link' => 'Link Kopiëren',
@@ -224,31 +225,31 @@ return [
     'pages_permissions_active' => 'Pagina Permissies Actief',
     'pages_initial_revision' => 'Eerste publicatie',
     'pages_initial_name' => 'Nieuwe Pagina',
-    'pages_editing_draft_notification' => 'You are currently editing a draft that was last saved :timeDiff.',
-    'pages_draft_edited_notification' => 'This page has been updated by since that time. It is recommended that you discard this draft.',
+    'pages_editing_draft_notification' => 'U bewerkt momenteel een concept dat voor het laatst is opgeslagen op :timeDiff.',
+    'pages_draft_edited_notification' => 'Deze pagina is sindsdien bijgewerkt. Het wordt aanbevolen dat u dit concept verwijderd.',
     'pages_draft_edit_active' => [
-        'start_a' => ':count users have started editing this page',
-        'start_b' => ':userName has started editing this page',
+        'start_a' => ':count gebruikers zijn begonnen deze pagina te bewerken',
+        'start_b' => ':userName is begonnen met het bewerken van deze pagina',
         'time_a' => 'since the pages was last updated',
-        'time_b' => 'in the last :minCount minutes',
-        'message' => ':start :time. Take care not to overwrite each other\'s updates!',
+        'time_b' => 'in de laatste :minCount minuten',
+        'message' => ':start :time. Let op om elkaars updates niet te overschrijven!',
     ],
-    'pages_draft_discarded' => 'Draft discarded, The editor has been updated with the current page content',
-    'pages_specific' => 'Specific Page',
-    'pages_is_template' => 'Page Template',
+    'pages_draft_discarded' => 'Concept verwijderd, de editor is bijgewerkt met de huidige pagina-inhoud',
+    'pages_specific' => 'Specifieke Pagina',
+    'pages_is_template' => 'Paginasjabloon',
 
     // Editor Sidebar
     'page_tags' => 'Pagina Labels',
-    'chapter_tags' => 'Chapter Tags',
-    'book_tags' => 'Book Tags',
-    'shelf_tags' => 'Shelf Tags',
+    'chapter_tags' => 'Tags van Hoofdstuk',
+    'book_tags' => 'Tags van Boeken',
+    'shelf_tags' => 'Tags van Boekplanken',
     'tag' => 'Label',
     'tags' =>  'Tags',
-    'tag_name' =>  'Tag Name',
+    'tag_name' =>  'Naam Tag',
     'tag_value' => 'Label Waarde (Optioneel)',
     'tags_explain' => "Voeg labels toe om de inhoud te categoriseren. \n Je kunt meerdere labels toevoegen.",
     'tags_add' => 'Voeg een extra label toe',
-    'tags_remove' => 'Remove this tag',
+    'tags_remove' => 'Deze tag verwijderen',
     'attachments' => 'Bijlages',
     'attachments_explain' => 'Upload bijlages of voeg een link toe. Deze worden zichtbaar in het navigatiepaneel.',
     'attachments_explain_instant_save' => 'Wijzigingen worden meteen opgeslagen.',
@@ -256,7 +257,7 @@ return [
     'attachments_upload' => 'Bestand Uploaden',
     'attachments_link' => 'Link Toevoegen',
     'attachments_set_link' => 'Zet Link',
-    'attachments_delete_confirm' => 'Klik opnieuw op \'verwijderen\' om de bijlage definitief te verwijderen.',
+    'attachments_delete' => 'Weet u zeker dat u deze bijlage wilt verwijderen?',
     'attachments_dropzone' => 'Sleep hier een bestand of klik hier om een bestand toe te voegen',
     'attachments_no_files' => 'Er zijn geen bestanden geüpload',
     'attachments_explain_link' => 'Je kunt een link toevoegen als je geen bestanden wilt uploaden. Dit kan een link naar een andere pagina op deze website zijn, maar ook een link naar een andere website.',
@@ -265,6 +266,7 @@ return [
     'attachments_link_url' => 'Link naar bestand',
     'attachments_link_url_hint' => 'Url, site of bestand',
     'attach' => 'Koppelen',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Bestand Bewerken',
     'attachments_edit_file_name' => 'Bestandsnaam',
     'attachments_edit_drop_upload' => 'Sleep een bestand of klik hier om te uploaden en te overschrijven',
@@ -274,12 +276,12 @@ return [
     'attachments_file_uploaded' => 'Bestand succesvol geüpload',
     'attachments_file_updated' => 'Bestand succesvol bijgewerkt',
     'attachments_link_attached' => 'Link successfully gekoppeld aan de pagina',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'templates' => 'Sjablonen',
+    'templates_set_as_template' => 'Pagina is een sjabloon',
+    'templates_explain_set_as_template' => 'Je kunt deze pagina als template instellen zodat de inhoud wordt gebruikt bij het maken van andere pagina\'s. Andere gebruikers kunnen deze template gebruiken als ze rechten hebben om deze pagina te bekijken.',
+    'templates_replace_content' => 'Pagina-inhoud vervangen',
+    'templates_append_content' => 'Toevoegen aan pagina-inhoud',
+    'templates_prepend_content' => 'Voeg vooraan toe aan pagina-inhoud',
 
     // Profile View
     'profile_user_for_x' => 'Lid sinds :time',
@@ -287,12 +289,12 @@ return [
     'profile_not_created_pages' => ':userName heeft geen pagina\'s gemaakt',
     'profile_not_created_chapters' => ':userName heeft geen hoofdstukken gemaakt',
     'profile_not_created_books' => ':userName heeft geen boeken gemaakt',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_not_created_shelves' => ':userName heeft nog geen boekenplanken gemaakt',
 
     // Comments
     'comment' => 'Reactie',
     'comments' => 'Reacties',
-    'comment_add' => 'Add Comment',
+    'comment_add' => 'Reactie Toevoegen',
     'comment_placeholder' => 'Laat hier een reactie achter',
     'comment_count' => '{0} Geen reacties|{1} 1 Reactie|[2,*] :count Reacties',
     'comment_save' => 'Sla reactie op',
@@ -309,7 +311,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Weet u zeker dat u deze revisie wilt verwijderen?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Weet u zeker dat u deze revisie wilt herstellen? De huidige pagina-inhoud wordt vervangen.',
     'revision_delete_success' => 'Revisie verwijderd',
     'revision_cannot_delete_latest' => 'Kan de laatste revisie niet verwijderen.'
 ];
\ No newline at end of file
index dca27ca396229562e3bc59cf578d1075fd630fb2..585346748a8825958efd06396f6f9af8048c257c 100644 (file)
@@ -13,7 +13,7 @@ return [
     'email_already_confirmed' => 'Het e-mailadres is al bevestigd. Probeer in te loggen.',
     'email_confirmation_invalid' => 'Deze bevestigingstoken is ongeldig, Probeer opnieuw te registreren.',
     'email_confirmation_expired' => 'De bevestigingstoken is verlopen, Een nieuwe bevestigingsmail is verzonden.',
-    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'email_confirmation_awaiting' => 'Het e-mail adres van dit account moet worden bevestigd',
     'ldap_fail_anonymous' => 'LDAP toegang kon geen \'anonymous bind\' uitvoeren',
     'ldap_fail_authed' => 'LDAP toegang was niet mogelijk met de opgegeven dn & wachtwoord',
     'ldap_extension_not_installed' => 'LDAP PHP-extensie is niet geïnstalleerd',
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Het uploaden van het bestand is verlopen.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Bij het bijwerken van de bijlage bleek de pagina onjuist',
     'attachment_not_found' => 'Bijlage niet gevonden',
 
     // Pages
@@ -83,21 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Pagina Niet Gevonden',
     'sorry_page_not_found' => 'Sorry, de pagina die je zocht is niet beschikbaar.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => 'Als u verwacht dat deze pagina bestaat heeft u misschien geen rechten om het te bekijken.',
     'return_home' => 'Terug naar home',
     'error_occurred' => 'Er Ging Iets Fout',
     'app_down' => ':appName is nu niet beschikbaar',
     'back_soon' => 'Komt snel weer online.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
-    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
-    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
-    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
-    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_no_authorization_found' => 'Geen autorisatie token gevonden',
+    'api_bad_authorization_format' => 'Een autorisatie token is gevonden, maar het formaat schijnt onjuist te zijn',
+    'api_user_token_not_found' => 'Er is geen overeenkomende API token gevonden voor de opgegeven autorisatie token',
+    'api_incorrect_token_secret' => 'Het opgegeven geheim voor de API token is onjuist',
+    'api_user_no_api_permission' => 'De eigenaar van de gebruikte API token heeft geen toestemming om API calls te maken',
+    'api_user_token_expired' => 'De gebruikte autorisatie token is verlopen',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => 'Fout opgetreden bij het verzenden van een test email:',
 
 ];
index e0f36816c12b4cbddaf722a0b6dd3dfb567251a8..4b27f03c20ac4372a8c379db1b15df72035c0ed1 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Wachtwoorden moeten overeenkomen en minimaal zes tekens lang zijn.',
     'user' => "We kunnen niemand vinden met dat e-mailadres.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Het wachtwoord reset token is ongeldig voor dit e-mailadres.',
     'sent' => 'We hebben je een link gestuurd om je wachtwoord te herstellen!',
     'reset' => 'Je wachtwoord is hersteld!',
 
index cdd1b6fef065da614dd5ab104491159d7c2d4c38..4c87095b4f8ce8304df6958add8b4871b7cda5cf 100644 (file)
@@ -44,9 +44,9 @@ return [
     // Color settings
     'content_colors' => 'Kleuren inhoud',
     'content_colors_desc' => 'Stelt de kleuren in voor alle elementen in de pagina-organisatieleiding. Het kiezen van kleuren met dezelfde helderheid als de standaard kleuren wordt aanbevolen voor de leesbaarheid.',
-    'bookshelf_color' => 'Shelf Color',
-    'book_color' => 'Book Color',
-    'chapter_color' => 'Chapter Color',
+    'bookshelf_color' => 'Kleur van de Boekenplank',
+    'book_color' => 'Kleur van het Boek',
+    'chapter_color' => 'Kleur van het Hoofdstuk',
     'page_color' => 'Pagina kleur',
     'page_draft_color' => 'Klad pagina kleur',
 
@@ -56,7 +56,7 @@ return [
     'reg_enable_toggle' => 'Registratie inschakelen',
     'reg_enable_desc' => 'Wanneer registratie is ingeschakeld, kan de gebruiker zich aanmelden als een gebruiker. Na registratie krijgen ze een enkele, standaard gebruikersrol.',
     'reg_default_role' => 'Standaard rol na registratie',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_enable_external_warning' => 'De optie hierboven wordt niet gebruikt terwijl LDAP authenticatie actief is. Gebruikersaccounts voor niet-bestaande leden zullen automatisch worden gecreëerd als authenticatie tegen het gebruikte LDAP-systeem succesvol is.',
     'reg_email_confirmation' => 'E-mail bevestiging',
     'reg_email_confirmation_toggle' => 'E-mailbevestiging verplichten',
     'reg_confirm_email_desc' => 'Als domeinrestricties aan staan dan is altijd e-maibevestiging nodig. Onderstaande instelling wordt dan genegeerd.',
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'E-mailbezorging lijkt te werken!',
     'maint_send_test_email_mail_text' => 'Gefeliciteerd! Nu je deze e-mailmelding hebt ontvangen, lijken je e-mailinstellingen correct te zijn geconfigureerd.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Rollen',
     'role_user_roles' => 'Gebruikrollen',
@@ -103,9 +117,10 @@ return [
     'role_manage_entity_permissions' => 'Beheer alle boeken-, hoofdstukken- en paginaresitrcties',
     'role_manage_own_entity_permissions' => 'Beheer restricties van je eigen boeken, hoofdstukken en pagina\'s',
     'role_manage_page_templates' => 'Paginasjablonen beheren',
-    'role_access_api' => 'Access system API',
+    'role_access_api' => 'Ga naar systeem API',
     'role_manage_settings' => 'Beheer app instellingen',
     'role_asset' => 'Asset Permissies',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Deze permissies bepalen de standaardtoegangsrechten. Permissies op boeken, hoofdstukken en pagina\'s overschrijven deze instelling.',
     'role_asset_admins' => 'Beheerders krijgen automatisch toegang tot alle inhoud, maar deze opties kunnen interface opties tonen of verbergen.',
     'role_all' => 'Alles',
@@ -160,11 +175,11 @@ return [
 
     // API Tokens
     'user_api_token_create' => 'Create API Token',
-    'user_api_token_name' => 'Name',
+    'user_api_token_name' => 'Naam',
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
-    'user_api_token_expiry' => 'Expiry Date',
+    'user_api_token_expiry' => 'Vervaldatum',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,9 +187,9 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
-    'user_api_token_delete' => 'Delete Token',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
+    'user_api_token_delete' => 'Token Verwijderen',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
     'user_api_token_delete_success' => 'API token successfully deleted',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index bb46cb2de9a00a05ee3dfb224bef3e355511b09e..01d74b99cddfbb90fc9bcaf5b278e2c7a14605f5 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Resetowanie hasła',
     'reset_password_send_instructions' => 'Wprowadź adres e-mail powiązany z Twoim kontem, by otrzymać link do resetowania hasła.',
     'reset_password_send_button' => 'Wyślij link do resetowania hasła',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Link z resetem hasła zostanie wysłany na :email jeśli mamy ten adres w systemie.',
     'reset_password_success' => 'Hasło zostało zresetowane pomyślnie.',
     'email_reset_subject' => 'Resetowanie hasła do :appName',
     'email_reset_text' => 'Otrzymujesz tę wiadomość ponieważ ktoś zażądał zresetowania hasła do Twojego konta.',
index 9fe31385832f428caadde57266384cdcc4abba51..c93d0b9e108f97638ffa8546cf70b66d90d9c8d0 100644 (file)
@@ -33,12 +33,13 @@ return [
     'copy' => 'Skopiuj',
     'reply' => 'Odpowiedz',
     'delete' => 'Usuń',
+    'delete_confirm' => 'Potwierdź usunięcie',
     'search' => 'Szukaj',
     'search_clear' => 'Wyczyść wyszukiwanie',
     'reset' => 'Resetuj',
     'remove' => 'Usuń',
     'add' => 'Dodaj',
-    'fullscreen' => 'Fullscreen',
+    'fullscreen' => 'Pełny ekran',
 
     // Sort Options
     'sort_options' => 'Opcje sortowania',
@@ -60,14 +61,14 @@ return [
     'grid_view' => 'Widok kafelkowy',
     'list_view' => 'Widok listy',
     'default' => 'Domyślny',
-    'breadcrumb' => 'Breadcrumb',
+    'breadcrumb' => 'Ścieżka nawigacji',
 
     // Header
     'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobacz profil',
     'edit_profile' => 'Edytuj profil',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Tryb ciemny',
+    'light_mode' => 'Tryb jasny',
 
     // Layout tabs
     'tab_info' => 'Informacje',
index b189c81710d136cc08b811e3707e8b29828ea454..b03ca35505ad6fb6008857f2989f95de86454773 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Wczytaj więcej',
     'image_image_name' => 'Nazwa obrazka',
     'image_delete_used' => 'Ten obrazek jest używany na stronach wyświetlonych poniżej.',
-    'image_delete_confirm' => 'Kliknij ponownie Usuń by potwierdzić usunięcie obrazka.',
+    'image_delete_confirm_text' => 'Czy na pewno chcesz usunąć ten obraz?',
     'image_select_image' => 'Wybierz obrazek',
     'image_dropzone' => 'Upuść obrazki tutaj lub kliknij by wybrać obrazki do przesłania',
     'images_deleted' => 'Usunięte obrazki',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edytuj kod',
     'code_language' => 'Język kodu',
     'code_content' => 'Zawartość kodu',
+    'code_session_history' => 'Historia sesji',
     'code_save' => 'Zapisz kod',
 ];
index 9a1b7f9d296f81681cf96a8c7c123152cb540b75..89a934291dfa06d0737a8da63fa75bb483be8702 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Brak stron spełniających zadane kryterium',
     'search_for_term' => 'Szukaj :term',
     'search_more' => 'Więcej wyników',
-    'search_filters' => 'Filtry wyszukiwania',
+    'search_advanced' => 'Wyszukiwanie zaawansowane',
+    'search_terms' => 'Szukane frazy',
     'search_content_type' => 'Rodzaj treści',
     'search_exact_matches' => 'Dokładne frazy',
     'search_tags' => 'Tagi wyszukiwania',
@@ -256,7 +257,7 @@ return [
     'attachments_upload' => 'Dodaj plik',
     'attachments_link' => 'Dodaj link',
     'attachments_set_link' => 'Ustaw link',
-    'attachments_delete_confirm' => 'Kliknij ponownie Usuń by potwierdzić usunięcie załącznika.',
+    'attachments_delete' => 'Jesteś pewien, że chcesz usunąć ten załącznik?',
     'attachments_dropzone' => 'Upuść pliki lub kliknij tutaj by przesłać pliki',
     'attachments_no_files' => 'Nie przesłano żadnych plików',
     'attachments_explain_link' => 'Możesz załączyć link jeśli nie chcesz przesyłać pliku. Może być to link do innej strony lub link do pliku w chmurze.',
@@ -265,6 +266,7 @@ return [
     'attachments_link_url' => 'Link do pliku',
     'attachments_link_url_hint' => 'Strona lub plik',
     'attach' => 'Załącz',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edytuj plik',
     'attachments_edit_file_name' => 'Nazwa pliku',
     'attachments_edit_drop_upload' => 'Upuść pliki lub kliknij tutaj by przesłać pliki i nadpisać istniejące',
index 5273906a021a79703df56435a181f1ef0017189c..5f5ec9db25150366d9c90c0f164814cb67a3386f 100644 (file)
@@ -13,7 +13,7 @@ return [
     'email_already_confirmed' => 'E-mail został potwierdzony, spróbuj się zalogować.',
     'email_confirmation_invalid' => 'Ten token jest nieprawidłowy lub został już wykorzystany. Spróbuj zarejestrować się ponownie.',
     'email_confirmation_expired' => 'Ten token potwierdzający wygasł. Wysłaliśmy Ci kolejny.',
-    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'email_confirmation_awaiting' => 'Adres e-mail dla używanego konta musi zostać potwierdzony',
     'ldap_fail_anonymous' => 'Dostęp LDAP przy użyciu anonimowego powiązania nie powiódł się',
     'ldap_fail_authed' => 'Dostęp LDAP przy użyciu tego DN i hasła nie powiódł się',
     'ldap_extension_not_installed' => 'Rozszerzenie LDAP PHP nie zostało zainstalowane',
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Przesyłanie pliku przekroczyło limit czasu.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Niezgodność strony podczas aktualizacji załącznika',
     'attachment_not_found' => 'Nie znaleziono załącznika',
 
     // Pages
@@ -83,21 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Strona nie została znaleziona',
     'sorry_page_not_found' => 'Przepraszamy, ale strona której szukasz nie została znaleziona.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => 'Jeśli spodziewałeś się, że ta strona istnieje, prawdopodobnie nie masz uprawnień do jej wyświetlenia.',
     'return_home' => 'Powrót do strony głównej',
     'error_occurred' => 'Wystąpił błąd',
     'app_down' => ':appName jest aktualnie wyłączona',
     'back_soon' => 'Niedługo zostanie uruchomiona ponownie.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
-    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
-    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
-    'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
-    'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_no_authorization_found' => 'Nie znaleziono tokenu autoryzacji dla żądania',
+    'api_bad_authorization_format' => 'Token autoryzacji został znaleziony w żądaniu, ale format okazał się nieprawidłowy',
+    'api_user_token_not_found' => 'Nie znaleziono pasującego tokenu API dla podanego tokenu autoryzacji',
+    'api_incorrect_token_secret' => 'Podany sekret dla tego API jest nieprawidłowy',
+    'api_user_no_api_permission' => 'Właściciel używanego tokenu API nie ma uprawnień do wykonywania zapytań do API',
+    'api_user_token_expired' => 'Token uwierzytelniania wygasł',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => 'Błąd podczas wysyłania testowej wiadomości e-mail:',
 
 ];
index 02f9201272abbbfb1a0e0a16e48373ac1f2aef07..7b67bf38de6a9dc724b7279c733d80357604761b 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Hasło musi zawierać co najmniej 6 znaków i być zgodne z powtórzeniem.',
     'user' => "Nie znaleziono użytkownika o takim adresie e-mail.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Token resetowania hasła jest nieprawidłowy dla tego adresu e-mail.',
     'sent' => 'Wysłaliśmy Ci link do resetowania hasła!',
     'reset' => 'Twoje hasło zostało zresetowane!',
 
index eefe2b19ed84c82f766007782a0c1e94b75d8731..4d7936e480876f025575e9c2b95fcc5eb50473f9 100644 (file)
@@ -56,7 +56,7 @@ return [
     'reg_enable_toggle' => 'Włącz rejestrację',
     'reg_enable_desc' => 'Kiedy rejestracja jest włączona użytkownicy mogą się rejestrować. Po rejestracji otrzymują jedną domyślną rolę użytkownika.',
     'reg_default_role' => 'Domyślna rola użytkownika po rejestracji',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_enable_external_warning' => 'Powyższa opcja jest ignorowana, gdy zewnętrzne uwierzytelnianie LDAP lub SAML jest aktywne. Konta użytkowników dla nieistniejących użytkowników zostaną automatycznie utworzone, jeśli uwierzytelnianie za pomocą systemu zewnętrznego zakończy się sukcesem.',
     'reg_email_confirmation' => 'Potwierdzenie adresu email',
     'reg_email_confirmation_toggle' => 'Wymagaj potwierdzenia adresu email',
     'reg_confirm_email_desc' => 'Jeśli restrykcje domenowe zostały ustawione, potwierdzenie adresu stanie się konieczne, a poniższa wartośc zostanie zignorowana.',
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Wygląda na to, że wysyłka wiadomości e-mail działa!',
     'maint_send_test_email_mail_text' => 'Gratulacje! Otrzymałeś tego e-maila więc Twoje ustawienia poczty elektronicznej wydają się być prawidłowo skonfigurowane.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Role',
     'role_user_roles' => 'Role użytkowników',
@@ -103,9 +117,10 @@ return [
     'role_manage_entity_permissions' => 'Zarządzanie uprawnieniami książek, rozdziałów i stron',
     'role_manage_own_entity_permissions' => 'Zarządzanie uprawnieniami własnych książek, rozdziałów i stron',
     'role_manage_page_templates' => 'Zarządzaj szablonami stron',
-    'role_access_api' => 'Access system API',
+    'role_access_api' => 'Dostęp do systemowego API',
     'role_manage_settings' => 'Zarządzanie ustawieniami aplikacji',
     'role_asset' => 'Zarządzanie zasobami',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Te ustawienia kontrolują zarządzanie zasobami systemu. Uprawnienia książek, rozdziałów i stron nadpisują te ustawienia.',
     'role_asset_admins' => 'Administratorzy mają automatycznie dostęp do wszystkich treści, ale te opcję mogą być pokazywać lub ukrywać opcje interfejsu użytkownika.',
     'role_all' => 'Wszyscy',
@@ -131,7 +146,7 @@ return [
     'users_send_invite_text' => 'Możesz wybrać wysłanie do tego użytkownika wiadomości e-mail z zaproszeniem, która pozwala mu ustawić własne hasło, w przeciwnym razie możesz ustawić je samemu.',
     'users_send_invite_option' => 'Wyślij e-mail z zaproszeniem',
     'users_external_auth_id' => 'Zewnętrzne identyfikatory autentykacji',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_external_auth_id_desc' => 'Jest to identyfikator używany do dopasowania tego użytkownika podczas komunikacji z zewnętrznym systemem uwierzytelniania.',
     'users_password_warning' => 'Wypełnij poniżej tylko jeśli chcesz zmienić swoje hasło:',
     'users_system_public' => 'Ten użytkownik reprezentuje każdego gościa odwiedzającego tę aplikację. Nie można się na niego zalogować, lecz jest przyznawany automatycznie.',
     'users_delete' => 'Usuń użytkownika',
@@ -141,7 +156,7 @@ return [
     'users_delete_success' => 'Użytkownik usunięty pomyślnie',
     'users_edit' => 'Edytuj użytkownika',
     'users_edit_profile' => 'Edytuj profil',
-    'users_edit_success' => 'Użytkownik zaktualizowany pomyśłnie',
+    'users_edit_success' => 'Użytkownik zaktualizowany pomyślnie',
     'users_avatar' => 'Avatar użytkownika',
     'users_avatar_desc' => 'Ten obrazek powinien posiadać wymiary 256x256px.',
     'users_preferred_language' => 'Preferowany język',
@@ -152,32 +167,32 @@ return [
     'users_social_disconnect' => 'Odłącz konto',
     'users_social_connected' => ':socialAccount zostało dodane do Twojego profilu.',
     'users_social_disconnected' => ':socialAccount zostało odłączone od Twojego profilu.',
-    'users_api_tokens' => 'API Tokens',
-    'users_api_tokens_none' => 'No API tokens have been created for this user',
-    'users_api_tokens_create' => 'Create Token',
-    'users_api_tokens_expires' => 'Expires',
-    'users_api_tokens_docs' => 'API Documentation',
+    'users_api_tokens' => 'Tokeny API',
+    'users_api_tokens_none' => 'Nie utworzono tokenów API dla tego użytkownika',
+    'users_api_tokens_create' => 'Utwórz token',
+    'users_api_tokens_expires' => 'Wygasa',
+    'users_api_tokens_docs' => 'Dokumentacja API',
 
     // API Tokens
-    'user_api_token_create' => 'Create API Token',
-    'user_api_token_name' => 'Name',
-    'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
-    'user_api_token_expiry' => 'Expiry Date',
-    'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
-    'user_api_token_create_success' => 'API token successfully created',
-    'user_api_token_update_success' => 'API token successfully updated',
-    'user_api_token' => 'API Token',
+    'user_api_token_create' => 'Utwórz klucz API',
+    'user_api_token_name' => 'Nazwa',
+    'user_api_token_name_desc' => 'Nadaj swojemu tokenowi czytelną nazwę jako opisującego jego cel.',
+    'user_api_token_expiry' => 'Data ważności',
+    'user_api_token_expiry_desc' => 'Ustaw datę, kiedy ten token wygasa. Po tej dacie żądania wykonane przy użyciu tego tokenu nie będą już działać. Pozostawienie tego pola pustego, ustawi ważność na 100 lat.',
+    'user_api_token_create_secret_message' => 'Natychmiast po utworzeniu tego tokenu zostanie wygenerowany i wyświetlony "Identyfikator tokenu"" i "Token Secret". Sekret zostanie wyświetlony tylko raz, więc przed kontynuacją upewnij się, że zostanie on skopiowany w bezpiecznie miejsce.',
+    'user_api_token_create_success' => 'Klucz API został poprawnie wygenerowany',
+    'user_api_token_update_success' => 'Klucz API został poprawnie zaktualizowany',
+    'user_api_token' => 'Token API',
     'user_api_token_id' => 'Token ID',
-    'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
-    'user_api_token_secret' => 'Token Secret',
-    'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
-    'user_api_token_delete' => 'Delete Token',
-    'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
-    'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
-    'user_api_token_delete_success' => 'API token successfully deleted',
+    'user_api_token_id_desc' => 'Jest to nieedytowalny identyfikator wygenerowany przez system dla tego tokenu, który musi być dostarczony w żądaniach API.',
+    'user_api_token_secret' => 'Token Api',
+    'user_api_token_secret_desc' => 'To jest wygenerowany przez system sekretny token, który musi być dostarczony w żądaniach API. Token zostanie wyświetlany tylko raz, więc skopiuj go w bezpiecznie miejsce.',
+    'user_api_token_created' => 'Token utworzony :timeAgo',
+    'user_api_token_updated' => 'Token zaktualizowany :timeAgo',
+    'user_api_token_delete' => 'Usuń token',
+    'user_api_token_delete_warning' => 'Spowoduje to całkowite usunięcie tokenu API o nazwie \':tokenName\' z systemu.',
+    'user_api_token_delete_confirm' => 'Czy jesteś pewien, że chcesz usunąć ten token?',
+    'user_api_token_delete_success' => 'Token API został poprawnie usunięty',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 68c58b92ba4e9243fd1afae7eca30ed89feab4df..e87bd11a5e343173fadf78e62a042e1ca0579a4a 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copy',
     'reply' => 'Reply',
     'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
     'reset' => 'Reset',
index d8e8981fb5fcf6ba8d15993453d4f8f2d07df970..48a0a32faa38c4821a9d71dda9a5fb4f97d35232 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Load More',
     'image_image_name' => 'Image Name',
     'image_delete_used' => 'This image is used in the pages below.',
-    'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Select Image',
     'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edit Code',
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
     'code_save' => 'Save Code',
 ];
index 6bbc723b0abfc1e6b1f69e270bbb9d2afdbe851b..f64867a56c31736a1730d58c51f3fe0c088364d1 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'No pages matched this search',
     'search_for_term' => 'Search for :term',
     'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Content Type',
     'search_exact_matches' => 'Exact Matches',
     'search_tags' => 'Tag Searches',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload File',
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'Url of site or file',
     'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
index 06a5285f56fc4ce11e6642549a1002b1bacae698..79024e482ed69efa633116592f9b7c83a0bcc93a 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
index f1345c743b6dcc2bdfc7555774627195ebcd4109..2bd314cf0f28561f9a2f296b373483df42480f89 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'User Roles',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Manage app settings',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'All',
index 094cdf4d2a423edcbdcc813b3aa9212e3655108f..b2c3072c41a8fe6af821064239c6ddc1511ae895 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Redefinir Senha',
     'reset_password_send_instructions' => 'Insira seu e-mail abaixo e uma mensagem com o link de redefinição de senha lhe será enviada.',
     'reset_password_send_button' => 'Enviar o Link de Redefinição',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Um link de redefinição de senha será enviado para :email se o endereço de e-mail for encontrado no sistema.',
     'reset_password_success' => 'Sua senha foi redefinida com sucesso.',
     'email_reset_subject' => 'Redefina a senha de :appName',
     'email_reset_text' => 'Você recebeu esse e-mail pois recebemos uma solicitação de redefinição de senha para a sua conta.',
index e57467ec11a9b93415fde5ac90058bff073cfd77..8b0dffe6e844cfbe55480cd7b7aaf57540df47d9 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copiar',
     'reply' => 'Responder',
     'delete' => 'Excluir',
+    'delete_confirm' => 'Confirmar Exclusão',
     'search' => 'Pesquisar',
     'search_clear' => 'Limpar Pesquisa',
     'reset' => 'Redefinir',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => 'Menu de Perfil',
     'view_profile' => 'Visualizar Perfil',
     'edit_profile' => 'Editar Perfil',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Modo Escuro',
+    'light_mode' => 'Modo Claro',
 
     // Layout tabs
     'tab_info' => 'Informações',
index e4510145294c1cd6a6cc0160dd02c69954d32566..a1210d52ce99c92a784e66a063fdb48a53e76a77 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Carregar Mais',
     'image_image_name' => 'Nome da Imagem',
     'image_delete_used' => 'Essa imagem é usada nas páginas abaixo.',
-    'image_delete_confirm' => 'Clique em Excluir novamente para confirmar que você deseja mesmo eliminar a imagem.',
+    'image_delete_confirm_text' => 'Tem certeza de que deseja excluir essa imagem?',
     'image_select_image' => 'Selecionar Imagem',
     'image_dropzone' => 'Arraste imagens ou clique aqui para fazer upload',
     'images_deleted' => 'Imagens Excluídas',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Editar Código',
     'code_language' => 'Linguagem do Código',
     'code_content' => 'Código',
+    'code_session_history' => 'Histórico de Sessão',
     'code_save' => 'Salvar Código',
 ];
index 323ce083f75a616d45ee6f6c0fa84043f4ff0258..b37d179314a27b261e89f91fa6ba50cb349db6f0 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Nenhuma página corresponde à pesquisa',
     'search_for_term' => 'Pesquisar por :term',
     'search_more' => 'Mais Resultados',
-    'search_filters' => 'Filtros de Pesquisa',
+    'search_advanced' => 'Pesquisa Avançada',
+    'search_terms' => 'Termos da Pesquisa',
     'search_content_type' => 'Tipo de Conteúdo',
     'search_exact_matches' => 'Correspondências Exatas',
     'search_tags' => 'Persquisar Tags',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload de Arquivos',
     'attachments_link' => 'Links Anexados',
     'attachments_set_link' => 'Definir Link',
-    'attachments_delete_confirm' => 'Clique novamente em Excluir para confirmar a exclusão desse anexo.',
+    'attachments_delete' => 'Tem certeza de que deseja excluir esse anexo?',
     'attachments_dropzone' => 'Arraste arquivos para cá ou clique para anexar arquivos',
     'attachments_no_files' => 'Nenhum arquivo foi enviado',
     'attachments_explain_link' => 'Você pode anexar um link se preferir não fazer o upload do arquivo. O link poderá ser para uma outra página ou para um arquivo na nuvem.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link para o Arquivo',
     'attachments_link_url_hint' => 'URL do site ou arquivo',
     'attach' => 'Anexar',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Editar Arquivo',
     'attachments_edit_file_name' => 'Nome do Arquivo',
     'attachments_edit_drop_upload' => 'Arraste arquivos para cá ou clique para anexar arquivos e sobrescreve-los',
index 0758c3e978ce0144ab25ec1795efe72ea8e8e9ff..c65d7f4969e14573eec31203e3f45a2dc955a0c8 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'O upload do arquivo expirou.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Erro de \'Page mismatch\' durante a atualização do anexo',
     'attachment_not_found' => 'Anexo não encontrado',
 
     // Pages
index 07865c43dd1345f68b3d95437d6bd4295a489213..fde9e2937e4bf04c0e784cd5f89da77be1fd046e 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Senhas devem ter ao menos oito caracteres e ser iguais à confirmação.',
     'user' => "Não pudemos encontrar um usuário com o e-mail fornecido.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'O token de redefinição de senha é inválido para este endereço de e-mail.',
     'sent' => 'Enviamos o link de redefinição de senha para o seu e-mail!',
     'reset' => 'Sua senha foi redefinida com sucesso!',
 
index 7176eb9f37e5064984226739e45608388de2e11a..a4da2ad880b6612eed9a163f97b5f17457ce3977 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'O envio de e-mails parece funcionar!',
     'maint_send_test_email_mail_text' => 'Parabéns! Já que você recebeu esta notificação, suas opções de e-mail parecem estar configuradas corretamente.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Cargos',
     'role_user_roles' => 'Cargos de Usuário',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Acessar API do sistema',
     'role_manage_settings' => 'Gerenciar configurações da aplicação',
     'role_asset' => 'Permissões de Ativos',
+    'roles_system_warning' => 'Esteja ciente de que o acesso a qualquer uma das três permissões acima pode permitir que um usuário altere seus próprios privilégios ou privilégios de outros usuários no sistema. Apenas atribua cargos com essas permissões para usuários confiáveis.',
     'role_asset_desc' => 'Essas permissões controlam o acesso padrão para os ativos dentro do sistema. Permissões em Livros, Capítulos e Páginas serão sobrescritas por essas permissões.',
     'role_asset_admins' => 'Administradores recebem automaticamente acesso a todo o conteúdo, mas essas opções podem mostrar ou ocultar as opções da Interface de Usuário.',
     'role_all' => 'Todos',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 12fac6507f82469f7f432f8ed5b46a5ca4372427..12eac6912df6f4ead8089965516d8c90ad58527b 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Скопировать',
     'reply' => 'Ответить',
     'delete' => 'Удалить',
+    'delete_confirm' => 'Подтвердить удаление',
     'search' => 'Поиск',
     'search_clear' => 'Очистить поиск',
     'reset' => 'Сбросить',
index d85fca0084358546f89ba300745cdecd58bb3b5f..12b1dd7cfbdd13723381068facf0aa7a66650953 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Загрузить еще',
     'image_image_name' => 'Название изображения',
     'image_delete_used' => 'Это изображение используется на странице ниже.',
-    'image_delete_confirm' => 'Нажмите \'Удалить\' еще раз для подтверждения удаления.',
+    'image_delete_confirm_text' => 'Вы уверены, что хотите удалить это изображение?',
     'image_select_image' => 'Выбрать изображение',
     'image_dropzone' => 'Перетащите изображение или кликните для загрузки',
     'images_deleted' => 'Изображения удалены',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Изменить код',
     'code_language' => 'Язык кода',
     'code_content' => 'Содержимое кода',
+    'code_session_history' => 'История сессии',
     'code_save' => 'Сохранить код',
 ];
index fc180061a86e20ae8f4ebb947b658de1dd708fa7..b27e041c28a880254c233055dd4cb14171b1811e 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Нет страниц, соответствующих этому поиску',
     'search_for_term' => 'Искать :term',
     'search_more' => 'Еще результаты',
-    'search_filters' => 'Фильтры поиска',
+    'search_advanced' => 'Расширенный поиск',
+    'search_terms' => 'Поисковые запросы',
     'search_content_type' => 'Тип содержимого',
     'search_exact_matches' => 'Точные соответствия',
     'search_tags' => 'Поиск по тегам',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Загрузить файл',
     'attachments_link' => 'Присоединить ссылку',
     'attachments_set_link' => 'Установить ссылку',
-    'attachments_delete_confirm' => 'Нажмите \'Удалить\' еще раз, чтобы подтвердить удаление этого файла.',
+    'attachments_delete' => 'Вы уверены, что хотите удалить это вложение?',
     'attachments_dropzone' => 'Перетащите файл сюда или нажмите здесь, чтобы загрузить файл',
     'attachments_no_files' => 'Файлы не загружены',
     'attachments_explain_link' => 'Вы можете присоединить ссылку, если вы предпочитаете не загружать файл. Это может быть ссылка на другую страницу или ссылка на файл в облаке.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Ссылка на файл',
     'attachments_link_url_hint' => 'URL-адрес сайта или файла',
     'attach' => 'Прикрепить',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Редактировать файл',
     'attachments_edit_file_name' => 'Название файла',
     'attachments_edit_drop_upload' => 'Перетащите файлы или нажмите здесь, чтобы загрузить и перезаписать',
index 0cc4a72b147727ca9acbbfbfec852852004f8f6c..e8f537ecdaa9e4b22b5b3fd467959cba88d09f66 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Время загрузки файла истекло.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Несоответствие страницы во время обновления вложения',
     'attachment_not_found' => 'Вложение не найдено',
 
     // Pages
index 0981aaa734dc8ea52a1d4918a0c07bf84fa8b697..b6dcf91dfad2e5b5b4e6f998c3aab60fd0f0ebb1 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Доставка электронной почты работает!',
     'maint_send_test_email_mail_text' => 'Поздравляем! Поскольку вы получили это письмо, электронная почта настроена правильно.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Роли',
     'role_user_roles' => 'Роли пользователя',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Доступ к системному API',
     'role_manage_settings' => 'Управление настройками приложения',
     'role_asset' => 'Права доступа к материалам',
+    'roles_system_warning' => 'Имейте в виду, что доступ к любому из указанных выше трех разрешений может позволить пользователю изменить свои собственные привилегии или привилегии других пользователей системы. Назначить роли с этими правами только доверенным пользователям.',
     'role_asset_desc' => 'Эти разрешения контролируют доступ по умолчанию к параметрам внутри системы. Разрешения на книги, главы и страницы перезапишут эти разрешения.',
     'role_asset_admins' => 'Администраторы автоматически получают доступ ко всему контенту, но эти опции могут отображать или скрывать параметры пользовательского интерфейса.',
     'role_all' => 'Все',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 444d78e95d45586755aecba945a578d8663bd084..f07506c2978895e26c865bcdd537842c46d3050d 100644 (file)
@@ -36,13 +36,13 @@ return [
     'book_sort_notification'      => 'Kniha úspešne znovu zoradená',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => 'vytvorená knižnica',
+    'bookshelf_create_notification'    => 'Knižnica úspešne vytvorená',
+    'bookshelf_update'                 => 'aktualizovaná knižnica',
+    'bookshelf_update_notification'    => 'Knižnica úspešne aktualizovaná',
+    'bookshelf_delete'                 => 'odstránená knižnica',
+    'bookshelf_delete_notification'    => 'Knižnica úspešne odstránená',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => 'komentované na',
 ];
index 2b2e83a179dc76f0b0618ad08b9525ff036270a3..ccde1f0405b159052bf84dd5ad73f0da4d7a2759 100644 (file)
@@ -18,7 +18,7 @@ return [
 
     'name' => 'Meno',
     'username' => 'Používateľské meno',
-    'email' => 'Email',
+    'email' => 'E-mail',
     'password' => 'Heslo',
     'password_confirm' => 'Potvrdiť heslo',
     'password_hint' => 'Musí mať viac ako 7 znakov',
@@ -26,8 +26,8 @@ return [
     'remember_me' => 'Zapamätať si ma',
     'ldap_email_hint' => 'Zadajte prosím email, ktorý sa má použiť pre tento účet.',
     'create_account' => 'Vytvoriť účet',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
+    'already_have_account' => 'Už máte svoj ​​účet?',
+    'dont_have_account' => 'Nemáte účet?',
     'social_login' => 'Sociálne prihlásenie',
     'social_registration' => 'Sociálna registrácia',
     'social_registration_text' => 'Registrovať sa a prihlásiť sa použitím inej služby.',
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Reset hesla',
     'reset_password_send_instructions' => 'Zadajte svoj email nižšie a bude Vám odoslaný email s odkazom pre reset hesla.',
     'reset_password_send_button' => 'Poslať odkaz na reset hesla',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Odkaz na obnovenie hesla bude odoslaný na :email, ak sa táto e-mailová adresa nachádza v systéme.',
     'reset_password_success' => 'Vaše heslo bolo úspešne resetované.',
     'email_reset_subject' => 'Reset Vášho :appName hesla',
     'email_reset_text' => 'Tento email Ste dostali pretože sme dostali požiadavku na reset hesla pre Váš účet.',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => 'Znova odoslať overovací email',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
+    'user_invite_email_subject' => 'Dostali ste pozvánku na pripojenie sa k aplikácii :appName!',
+    'user_invite_email_greeting' => 'Účet pre :appName bol pre vás vytvorený.',
+    'user_invite_email_text' => 'Kliknutím na tlačidlo nižšie nastavíte heslo k účtu a získate prístup:',
+    'user_invite_email_action' => 'Heslo k účtu',
+    'user_invite_page_welcome' => 'Vitajte na stránke :appName!',
+    'user_invite_page_text' => 'Ak chcete dokončiť svoj účet a získať prístup, musíte nastaviť heslo, ktoré sa použije na prihlásenie do aplikácie :appName pri budúcich návštevách.',
+    'user_invite_page_confirm_button' => 'Potvrdiť heslo',
+    'user_invite_success' => 'Nastavené heslo, teraz máte prístup k :appName!'
 ];
\ No newline at end of file
index 5674fd5a72a56790b52a7fff998c2dda7ae24724..555c2c6f2c65c71427763c8f52f17f2b4554dd74 100644 (file)
@@ -11,8 +11,8 @@ return [
     'save' => 'Uložiť',
     'continue' => 'Pokračovať',
     'select' => 'Vybrať',
-    'toggle_all' => 'Toggle All',
-    'more' => 'More',
+    'toggle_all' => 'Prepnúť všetko',
+    'more' => 'Viac',
 
     // Form Labels
     'name' => 'Meno',
@@ -24,30 +24,31 @@ return [
     // Actions
     'actions' => 'Akcie',
     'view' => 'Zobraziť',
-    'view_all' => 'View All',
+    'view_all' => 'Zobraziť všetko',
     'create' => 'Vytvoriť',
     'update' => 'Aktualizovať',
     'edit' => 'Editovať',
     'sort' => 'Zoradiť',
     'move' => 'Presunúť',
-    'copy' => 'Copy',
-    'reply' => 'Reply',
+    'copy' => 'Kopírovať',
+    'reply' => 'Odpovedať',
     'delete' => 'Zmazať',
-    'search' => 'Hľadť',
+    'delete_confirm' => 'Potvrdiť zmazanie',
+    'search' => 'Hľadať',
     'search_clear' => 'Vyčistiť hľadanie',
-    'reset' => 'Reset',
+    'reset' => 'Resetovať',
     'remove' => 'Odstrániť',
-    'add' => 'Add',
-    'fullscreen' => 'Fullscreen',
+    'add' => 'Pridať',
+    'fullscreen' => 'Celá obrazovka',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => 'Možnosti triedenia',
+    'sort_direction_toggle' => 'Zoradiť smerový prepínač',
+    'sort_ascending' => 'Zoradiť vzostupne',
+    'sort_descending' => 'Zoradiť zostupne',
+    'sort_name' => 'Meno',
+    'sort_created_at' => 'Dátum vytvorenia',
+    'sort_updated_at' => 'Aktualizované dňa',
 
     // Misc
     'deleted_user' => 'Odstránený používateľ',
@@ -56,22 +57,22 @@ return [
     'back_to_top' => 'Späť nahor',
     'toggle_details' => 'Prepnúť detaily',
     'toggle_thumbnails' => 'Prepnúť náhľady',
-    'details' => 'Details',
-    'grid_view' => 'Grid View',
-    'list_view' => 'List View',
-    'default' => 'Default',
+    'details' => 'Podrobnosti',
+    'grid_view' => 'Zobrazenie v mriežke',
+    'list_view' => 'Zobraziť ako zoznam',
+    'default' => 'Predvolené',
     'breadcrumb' => 'Breadcrumb',
 
     // Header
-    'profile_menu' => 'Profile Menu',
+    'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobraziť profil',
     'edit_profile' => 'Upraviť profil',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Tmavý režim',
+    'light_mode' => 'Svetlý režim',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => 'Informácie',
+    'tab_content' => 'Obsah',
 
     // Email Content
     'email_action_help' => 'Ak máte problém klinkúť na tlačidlo ":actionText", skopírujte a vložte URL uvedenú nižšie do Vášho prehliadača:',
index 726d8361438ec26712eaaf5611b8de49868a16b7..a6bae619c47be8ecc19bf88725909c61634d2985 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Načítať viac',
     'image_image_name' => 'Názov obrázka',
     'image_delete_used' => 'Tento obrázok je použitý na stránkach uvedených nižšie.',
-    'image_delete_confirm' => 'Kliknite znova na zmazať pre potvrdenie zmazania tohto obrázka.',
+    'image_delete_confirm_text' => 'Naozaj chcete vymazať tento obrázok?',
     'image_select_image' => 'Vybrať obrázok',
     'image_dropzone' => 'Presuňte obrázky sem alebo kliknite sem pre nahranie',
     'images_deleted' => 'Obrázky zmazané',
@@ -23,11 +23,12 @@ return [
     'image_upload_success' => 'Obrázok úspešne nahraný',
     'image_update_success' => 'Detaily obrázka úspešne aktualizované',
     'image_delete_success' => 'Obrázok úspešne zmazaný',
-    'image_upload_remove' => 'Remove',
+    'image_upload_remove' => 'Odstrániť',
 
     // Code Editor
-    'code_editor' => 'Edit Code',
-    'code_language' => 'Code Language',
-    'code_content' => 'Code Content',
-    'code_save' => 'Save Code',
+    'code_editor' => 'Upraviť kód',
+    'code_language' => 'Kód jazyka',
+    'code_content' => 'Obsah kódu',
+    'code_session_history' => 'História relácií',
+    'code_save' => 'Ulož kód',
 ];
index 2b42ca86cd43c89238b18d808ad1411c4807b8dd..1e6bf4972b4ad41fa29c2acbcef22c09d595d2d6 100644 (file)
@@ -11,13 +11,13 @@ return [
     'recently_updated_pages' => 'Nedávno aktualizované stránky',
     'recently_created_chapters' => 'Nedávno vytvorené kapitoly',
     'recently_created_books' => 'Nedávno vytvorené knihy',
-    'recently_created_shelves' => 'Recently Created Shelves',
+    'recently_created_shelves' => 'Nedávno vytvorené knižnice',
     'recently_update' => 'Nedávno aktualizované',
     'recently_viewed' => 'Nedávno zobrazené',
     'recent_activity' => 'Nedávna aktivita',
     'create_now' => 'Vytvoriť teraz',
     'revisions' => 'Revízie',
-    'meta_revision' => 'Revision #:revisionCount',
+    'meta_revision' => 'Upravené vydanie #:revisionCount',
     'meta_created' => 'Vytvorené :timeLength',
     'meta_created_name' => 'Vytvorené :timeLength používateľom :user',
     'meta_updated' => 'Aktualizované :timeLength',
@@ -29,8 +29,8 @@ return [
     'no_pages_viewed' => 'Nepozreli ste si žiadne stránky',
     'no_pages_recently_created' => 'Žiadne stránky neboli nedávno vytvorené',
     'no_pages_recently_updated' => 'Žiadne stránky neboli nedávno aktualizované',
-    'export' => 'Export',
-    'export_html' => 'Contained Web File',
+    'export' => 'Exportovať',
+    'export_html' => 'Obsahovaný webový súbor',
     'export_pdf' => 'PDF súbor',
     'export_text' => 'Súbor s čistým textom',
 
@@ -42,56 +42,57 @@ return [
 
     // Search
     'search_results' => 'Výsledky hľadania',
-    'search_total_results_found' => ':count result found|:count total results found',
+    'search_total_results_found' => ':count výsledok found|:počet nájdených výsledkov',
     'search_clear' => 'Vyčistiť hľadanie',
     'search_no_pages' => 'Žiadne stránky nevyhovujú tomuto hľadaniu',
     'search_for_term' => 'Hľadať :term',
-    'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
-    'search_content_type' => 'Content Type',
-    'search_exact_matches' => 'Exact Matches',
-    'search_tags' => 'Tag Searches',
-    'search_options' => 'Options',
-    'search_viewed_by_me' => 'Viewed by me',
-    'search_not_viewed_by_me' => 'Not viewed by me',
-    'search_permissions_set' => 'Permissions set',
-    'search_created_by_me' => 'Created by me',
-    'search_updated_by_me' => 'Updated by me',
-    'search_date_options' => 'Date Options',
-    'search_updated_before' => 'Updated before',
-    'search_updated_after' => 'Updated after',
-    'search_created_before' => 'Created before',
-    'search_created_after' => 'Created after',
-    'search_set_date' => 'Set Date',
-    'search_update' => 'Update Search',
+    'search_more' => 'Načítať ďalšie výsledky',
+    'search_advanced' => 'Rozšírené vyhľadávanie',
+    'search_terms' => 'Hľadané výrazy',
+    'search_content_type' => 'Typ obsahu',
+    'search_exact_matches' => 'Presná zhoda',
+    'search_tags' => 'Vyhľadávanie značiek',
+    'search_options' => 'Možnosti',
+    'search_viewed_by_me' => 'Videné mnou',
+    'search_not_viewed_by_me' => 'Nevidené mnou',
+    'search_permissions_set' => 'Oprávnenia',
+    'search_created_by_me' => 'Vytvorené mnou',
+    'search_updated_by_me' => 'Aktualizované mnou',
+    'search_date_options' => 'Možnosti dátumu',
+    'search_updated_before' => 'Aktualizované pred',
+    'search_updated_after' => 'Aktualizované po',
+    'search_created_before' => 'Vytvorené pred',
+    'search_created_after' => 'Vytvorené po',
+    'search_set_date' => 'Nastaviť Dátum',
+    'search_update' => 'Aktualizujte vyhľadávanie',
 
     // 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',
-    'shelves_books' => 'Books on this shelf',
-    'shelves_add_books' => 'Add books to this shelf',
-    'shelves_drag_books' => 'Drag books here to add them to this shelf',
-    'shelves_empty_contents' => 'This shelf has no books assigned to it',
-    'shelves_edit_and_assign' => 'Edit shelf to assign books',
-    'shelves_edit_named' => 'Edit Bookshelf :name',
-    'shelves_edit' => 'Edit Bookshelf',
-    'shelves_delete' => 'Delete Bookshelf',
-    'shelves_delete_named' => 'Delete Bookshelf :name',
-    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
-    'shelves_delete_confirmation' => 'Are you sure you want to delete this bookshelf?',
-    'shelves_permissions' => 'Bookshelf Permissions',
-    'shelves_permissions_updated' => 'Bookshelf Permissions Updated',
-    'shelves_permissions_active' => 'Bookshelf Permissions Active',
+    'shelf' => 'Polica',
+    'shelves' => 'Police',
+    'x_shelves' => ':count Shelf|:count Police',
+    'shelves_long' => 'Poličky na knihy',
+    'shelves_empty' => 'Neboli vytvorené žiadne police',
+    'shelves_create' => 'Vytvoriť novú policu',
+    'shelves_popular' => 'Populárne police',
+    'shelves_new' => 'Nové police',
+    'shelves_new_action' => 'Nová polica',
+    'shelves_popular_empty' => 'Najpopulárnejšie police sa objavia tu.',
+    'shelves_new_empty' => 'Najpopulárnejšie police sa objavia tu.',
+    'shelves_save' => 'Uložiť policu',
+    'shelves_books' => 'Knihy na tejto polici',
+    'shelves_add_books' => 'Pridať knihy do tejto police',
+    'shelves_drag_books' => 'Potiahnite knihy sem a pridajte ich do tejto police',
+    'shelves_empty_contents' => 'Táto polica nemá priradené žiadne knihy',
+    'shelves_edit_and_assign' => 'Uprav policu a priraď knihy',
+    'shelves_edit_named' => 'Upraviť poličku::name',
+    'shelves_edit' => 'Upraviť policu',
+    'shelves_delete' => 'Odstrániť knižnicu',
+    'shelves_delete_named' => 'Odstrániť knižnicu :name',
+    'shelves_delete_explain' => "Týmto vymažete policu s názvom ': name'. Obsahované knihy sa neodstránia.",
+    'shelves_delete_confirmation' => 'Ste si istý, že chcete zmazať túto knižnicu?',
+    'shelves_permissions' => 'Oprávnenia knižnice',
+    'shelves_permissions_updated' => 'Oprávnenia knižnice aktualizované',
+    'shelves_permissions_active' => 'Oprávnenia knižnice aktívne',
     'shelves_copy_permissions_to_books' => 'Copy Permissions to Books',
     'shelves_copy_permissions' => 'Copy Permissions',
     'shelves_copy_permissions_explain' => 'This will apply the current permission settings of this bookshelf to all books contained within. Before activating, ensure any changes to the permissions of this bookshelf have been saved.',
@@ -256,7 +257,7 @@ return [
     'attachments_upload' => 'Nahrať súbor',
     'attachments_link' => 'Priložiť odkaz',
     'attachments_set_link' => 'Nastaviť odkaz',
-    'attachments_delete_confirm' => 'Kliknite znova na zmazať pre potvrdenie zmazania prílohy.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Presuňte súbory alebo klinknite sem pre priloženie súboru',
     'attachments_no_files' => 'Žiadne súbory neboli nahrané',
     'attachments_explain_link' => 'Ak nechcete priložiť súbor, môžete priložiť odkaz. Môže to byť odkaz na inú stránku alebo odkaz na súbor v cloude.',
@@ -265,6 +266,7 @@ return [
     'attachments_link_url' => 'Odkaz na súbor',
     'attachments_link_url_hint' => 'Url stránky alebo súboru',
     'attach' => 'Priložiť',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Upraviť súbor',
     'attachments_edit_file_name' => 'Názov súboru',
     'attachments_edit_drop_upload' => 'Presuňte súbory sem alebo klinknite pre nahranie a prepis',
@@ -304,12 +306,12 @@ return [
     'comment_deleted_success' => 'Comment deleted',
     'comment_created_success' => 'Comment added',
     'comment_updated_success' => 'Comment updated',
-    'comment_delete_confirm' => 'Are you sure you want to delete this comment?',
-    'comment_in_reply_to' => 'In reply to :commentId',
+    'comment_delete_confirm' => 'Ste si istý, že chcete odstrániť tento komentár?',
+    'comment_in_reply_to' => 'Odpovedať na :commentId',
 
     // Revision
     'revision_delete_confirm' => 'Naozaj chcete túto revíziu odstrániť?',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
+    'revision_restore_confirm' => 'Naozaj chcete obnoviť túto revíziu? Aktuálny obsah stránky sa nahradí.',
     'revision_delete_success' => 'Revízia bola vymazaná',
     'revision_cannot_delete_latest' => 'Nie je možné vymazať poslednú revíziu.'
 ];
\ No newline at end of file
index 3b52a667c3675c29c8536612364c63e19701aa95..c045d473d05e99e86971523018513dc50de667d8 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Nahrávanie súboru vypršalo.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
index 30ed1b78220897b6f9ec530a163010cbd71dea2c..5b80f62da63877abbf0ebb07084bcfb6efd14aa7 100644 (file)
@@ -6,9 +6,9 @@
  */
 return [
 
-    'password' => 'Heslo musí obsahovať aspoň šesť znakov a musí byť rovnaké ako potvrdzujúce.',
+    'password' => 'Heslo musí obsahovať aspoň osem znakov a musí byť rovnaké ako potvrdzujúce.',
     'user' => "Nenašli sme používateľa s takou emailovou adresou.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Token na obnovenie hesla je pre túto e-mailovú adresu neplatný.',
     'sent' => 'Poslali sme Vám email s odkazom na reset hesla!',
     'reset' => 'Vaše heslo bolo resetované!',
 
index 75a847b0aaa58a252644539d5f25488c1ca75476..8b16157982d9ba60a609085d6ac41840d80c42d4 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roly',
     'role_user_roles' => 'Používateľské roly',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Spravovať nastavenia aplikácie',
     'role_asset' => 'Oprávnenia majetku',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Tieto oprávnenia regulujú prednastavený prístup k zdroju v systéme. Oprávnenia pre knihy, kapitoly a stránky majú vyššiu prioritu.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'Všetko',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 9c1480d057205f8010d51031a60747b923780cb8..527ecace5457215c72d13fb73070a238dc902409 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopiraj',
     'reply' => 'Odgovori',
     'delete' => 'Izbriši',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Išči',
     'search_clear' => 'Počisti iskanje',
     'reset' => 'Ponastavi',
index 42e87be318f2eec3937f8983287d683dd33d76e3..658859e7ef97b4ae104bb4eba8798ae3fa8884b3 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Naloži več',
     'image_image_name' => 'Ime slike',
     'image_delete_used' => 'Ta slika je uporabljena na spodnjih straneh.',
-    'image_delete_confirm' => 'Ponovno kliknite izbriši, da potrdite izbris te slike.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Izberite sliko',
     'image_dropzone' => 'Povlecite slike ali kliknite tukaj za nalaganje',
     'images_deleted' => 'Slike so bile izbrisane',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Uredi kodo',
     'code_language' => 'Koda jezika',
     'code_content' => 'Koda vsebine',
+    'code_session_history' => 'Session History',
     'code_save' => 'Shrani kodo',
 ];
index ff1382159e38295bd0337803a975506a628e26f0..40d891f878b74c1b340f546cc073313863bbef8d 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Nobena stran se ne ujema z vašim iskanjem',
     'search_for_term' => 'Išči :term',
     'search_more' => 'Prikaži več rezultatov',
-    'search_filters' => 'Iskalni filtri',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Vrsta vsebine',
     'search_exact_matches' => 'Natančno ujemanje',
     'search_tags' => 'Iskanje oznak',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Naloži datoteko',
     'attachments_link' => 'Pripni povezavo',
     'attachments_set_link' => 'Nastavi povezavo',
-    'attachments_delete_confirm' => 'Ponovno kliknite izbriši, da potrdite izbris te priloge.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Spustite datoteke ali kliknite tukaj, če želite priložiti datoteko',
     'attachments_no_files' => 'Nobena datoteka ni bila naložena',
     'attachments_explain_link' => 'Lahko pripnete povezavo, če ne želite naložiti datoteke. Lahko je povezava na drugo stran ali povezava do dateteke v oblaku.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Povezava do datoteke',
     'attachments_link_url_hint' => 'Url mesta ali datoteke',
     'attach' => 'Pripni',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Uredi datoteko',
     'attachments_edit_file_name' => 'Ime datoteke',
     'attachments_edit_drop_upload' => 'Spustite datoteke ali kliknite tukaj, če želite naložiti in prepisati',
index bf1564eefd9228c6d7d615ef614cff1907049d6e..e8ea99ab70e5aad12701c279e499d1610edcb06d 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Čas nalaganjanja datoteke je potekel.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Neskladje strani med posodobitvijo priloge',
     'attachment_not_found' => 'Priloga ni najdena',
 
     // Pages
index 24cd9229a5a0a966c7704c7ada969d3cec87fe72..5d0c0ba7427eeb643d954250d5321eed3e069f7c 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Zdi se, da dostava e-pošte deluje!',
     'maint_send_test_email_mail_text' => 'Čestitke! Če ste prejeli e.poštno obvestilo so bile vaše e-poštne nastavitve pravilno konfigurirane.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Vloge',
     'role_user_roles' => 'Pravilo uporabnika',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Nastavitve za upravljanje',
     'role_asset' => 'Sistemska dovoljenja',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'Vse',
@@ -168,7 +183,7 @@ V tej vlogi trenutno ni dodeljen noben uporabnik',
     'user_api_token_expiry' => 'Datum poteka',
     'user_api_token_expiry_desc' => 'Določi datum izteka uporabnosti žetona. Po tem datumu, zahteve poslane s tem žetonom, ne bodo več delovale. 
 Če pustite to polje prazno, bo iztek uporabnosti 100.let .',
-    'user_api_token_create_secret_message' => 'Takoj po ustvarjanju tega žetona se ustvari in prikaže "Token ID" "in" Token Secret ". Skrivnost  bo prikazana samo enkrat, zato se pred nadaljevanjem prepričajte o varnosti kopirnega mesta.',
+    'user_api_token_create_secret_message' => 'Takoj po ustvarjanju tega žetona se ustvari in prikaže "Token ID" "in" Token Secret ". Skrivnost bo prikazana samo enkrat, zato se pred nadaljevanjem prepričajte o varnosti kopirnega mesta.',
     'user_api_token_create_success' => 'API žeton uspešno ustvarjen',
     'user_api_token_update_success' => 'API žeton uspešno posodobljen',
     'user_api_token' => 'API žeton',
@@ -189,6 +204,7 @@ V tej vlogi trenutno ni dodeljen noben uporabnik',
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'danščina',
         'de' => 'Deutsch (Sie)',
index 9b91e2b5ae60b9a9163f893b8a6e76bb2767382d..f6acd332fbc91dabc9351af1d1a715529c6a648a 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopiera',
     'reply' => 'Svara',
     'delete' => 'Ta bort',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Sök',
     'search_clear' => 'Rensa sökning',
     'reset' => 'Återställ',
index 5e4085dec792eac2d657d2f016418e76eefa3892..aef0400d26b97f9ce96ae37bb177e25f7d10eb60 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Ladda fler',
     'image_image_name' => 'Bildnamn',
     'image_delete_used' => 'Den här bilden används på nedanstående sidor.',
-    'image_delete_confirm' => 'Klicka på "ta bort" en gång till för att bekräfta att du vill ta bort bilden.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Välj bild',
     'image_dropzone' => 'Släpp bilder här eller klicka för att ladda upp',
     'images_deleted' => 'Bilder borttagna',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Redigera kod',
     'code_language' => 'Språk',
     'code_content' => 'Kod',
+    'code_session_history' => 'Session History',
     'code_save' => 'Spara',
 ];
index e4938fbfd542426543025997042482452a840c4d..b71a27ccfbf8fd5c5c254b1b98ec46b939971619 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Inga sidor matchade sökningen',
     'search_for_term' => 'Sök efter :term',
     'search_more' => 'Fler resultat',
-    'search_filters' => 'Sökfilter',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Innehållstyp',
     'search_exact_matches' => 'Exakta matchningar',
     'search_tags' => 'Taggar',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Ladda upp fil',
     'attachments_link' => 'Bifoga länk',
     'attachments_set_link' => 'Ange länk',
-    'attachments_delete_confirm' => 'Klicka på "ta bort" igen för att bekräfta att du vill ta bort bilagan.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Släpp filer här eller klicka för att ladda upp',
     'attachments_no_files' => 'Inga filer har laddats upp',
     'attachments_explain_link' => 'Du kan bifoga en länk om du inte vill ladda upp en fil. Detta kan vara en länk till en annan sida eller till en fil i molnet.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Länk till fil',
     'attachments_link_url_hint' => 'URL till sida eller fil',
     'attach' => 'Bifoga',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Redigera fil',
     'attachments_edit_file_name' => 'Filnamn',
     'attachments_edit_drop_upload' => 'Släpp filer här eller klicka för att ladda upp och skriva över',
index adf22af1d18f9f50c7d221083bb5b0d8e5730256..dbc3f37f237021350066bfe1fc36a29e4be4566e 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Filuppladdningen har tagits ut.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Fel i sidmatchning vid uppdatering av bilaga',
     'attachment_not_found' => 'Bilagan hittades ej',
 
     // Pages
index 488f52cc3c054207ea6911ef80bd00dd4b428085..1b2f33bdecde75a73191df467479a3a86630e357 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'E-postleverans verkar fungera!',
     'maint_send_test_email_mail_text' => 'Grattis! Eftersom du fick detta e-postmeddelande verkar dina e-postinställningar vara korrekt konfigurerade.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roller',
     'role_user_roles' => 'Användarroller',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Åtkomst till systemets API',
     'role_manage_settings' => 'Hantera appinställningar',
     'role_asset' => 'Tillgång till innehåll',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Det här är standardinställningarna för allt innehåll i systemet. Eventuella anpassade rättigheter på böcker, kapitel och sidor skriver över dessa inställningar.',
     'role_asset_admins' => 'Administratörer har automatisk tillgång till allt innehåll men dessa alternativ kan visa och dölja vissa gränssnittselement',
     'role_all' => 'Alla',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Danska',
         'de' => 'Deutsch (Sie)',
index 68c58b92ba4e9243fd1afae7eca30ed89feab4df..e87bd11a5e343173fadf78e62a042e1ca0579a4a 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Copy',
     'reply' => 'Reply',
     'delete' => 'Delete',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Search',
     'search_clear' => 'Clear Search',
     'reset' => 'Reset',
index d8e8981fb5fcf6ba8d15993453d4f8f2d07df970..48a0a32faa38c4821a9d71dda9a5fb4f97d35232 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Load More',
     'image_image_name' => 'Image Name',
     'image_delete_used' => 'This image is used in the pages below.',
-    'image_delete_confirm' => 'Click delete again to confirm you want to delete this image.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Select Image',
     'image_dropzone' => 'Drop images or click here to upload',
     'images_deleted' => 'Images Deleted',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Edit Code',
     'code_language' => 'Code Language',
     'code_content' => 'Code Content',
+    'code_session_history' => 'Session History',
     'code_save' => 'Save Code',
 ];
index 6bbc723b0abfc1e6b1f69e270bbb9d2afdbe851b..f64867a56c31736a1730d58c51f3fe0c088364d1 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'No pages matched this search',
     'search_for_term' => 'Search for :term',
     'search_more' => 'More Results',
-    'search_filters' => 'Search Filters',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Content Type',
     'search_exact_matches' => 'Exact Matches',
     'search_tags' => 'Tag Searches',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Upload File',
     'attachments_link' => 'Attach Link',
     'attachments_set_link' => 'Set Link',
-    'attachments_delete_confirm' => 'Click delete again to confirm you want to delete this attachment.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Drop files or click here to attach a file',
     'attachments_no_files' => 'No files have been uploaded',
     'attachments_explain_link' => 'You can attach a link if you\'d prefer not to upload a file. This can be a link to another page or a link to a file in the cloud.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Link to file',
     'attachments_link_url_hint' => 'Url of site or file',
     'attach' => 'Attach',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'File Name',
     'attachments_edit_drop_upload' => 'Drop files or click here to upload and overwrite',
index 06a5285f56fc4ce11e6642549a1002b1bacae698..79024e482ed69efa633116592f9b7c83a0bcc93a 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'The file upload has timed out.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Page mismatch during attachment update',
     'attachment_not_found' => 'Attachment not found',
 
     // Pages
index f1345c743b6dcc2bdfc7555774627195ebcd4109..2bd314cf0f28561f9a2f296b373483df42480f89 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Email delivery seems to work!',
     'maint_send_test_email_mail_text' => 'Congratulations! As you received this email notification, your email settings seem to be configured properly.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roles',
     'role_user_roles' => 'User Roles',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Manage app settings',
     'role_asset' => 'Asset Permissions',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'These permissions control default access to the assets within the system. Permissions on Books, Chapters and Pages will override these permissions.',
     'role_asset_admins' => 'Admins are automatically given access to all content but these options may show or hide UI options.',
     'role_all' => 'All',
index 587640672fb289ad10985bf09fb66b4f2047b0a2..a252ac7703d6e9b65f8eb0f010e8e3fa75ebb972 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Kopyala',
     'reply' => 'Yanıtla',
     'delete' => 'Sil',
+    'delete_confirm' => 'Silmeyi Onayla',
     'search' => 'Ara',
     'search_clear' => 'Aramayı Temizle',
     'reset' => 'Sıfırla',
index 4f15735410ce192a9bc4083a0a8d22d7830ce064..009c48f23bb8f1bd495acc4ad6fcd73859056455 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Devamını Göster',
     'image_image_name' => 'Görsel Adı',
     'image_delete_used' => 'Bu görsel aşağıda bulunan sayfalarda kullanılmış.',
-    'image_delete_confirm' => 'Bu görseli silmek istediğinize emin misiniz?',
+    'image_delete_confirm_text' => 'Bu resmi silmek istediğinizden emin misiniz?',
     'image_select_image' => 'Görsel Seç',
     'image_dropzone' => 'Görselleri sürükleyin ya da seçin',
     'images_deleted' => 'Görseller Silindi',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Kodu Düzenle',
     'code_language' => 'Kod Dili',
     'code_content' => 'Kod İçeriği',
+    'code_session_history' => 'Oturum Geçmişi',
     'code_save' => 'Kodu Kaydet',
 ];
index df0dadb8927e6d3843f614a069f0801ee68650f3..cf2612640d14489b0f9b6efbecd0971fc268f9ed 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Bu aramayla ilgili herhangi bir sayfa bulunamadı',
     'search_for_term' => ':term için Ara',
     'search_more' => 'Daha Fazla Sonuç',
-    'search_filters' => 'Arama Filtreleri',
+    'search_advanced' => 'Gelişmiş Arama',
+    'search_terms' => 'Terimleri Ara',
     'search_content_type' => 'İçerik Türü',
     'search_exact_matches' => 'Tam Eşleşmeler',
     'search_tags' => 'Etiket Aramaları',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Dosya Yükle',
     'attachments_link' => 'Link Ekle',
     'attachments_set_link' => 'Bağlantıyı Ata',
-    'attachments_delete_confirm' => 'Eki silmek istediğinize emin misiniz?',
+    'attachments_delete' => 'Bu eki silmek istediğinize emin misiniz?',
     'attachments_dropzone' => 'Dosyaları sürükleyin veya seçin',
     'attachments_no_files' => 'Hiçbir dosya yüklenmedi',
     'attachments_explain_link' => 'Eğer dosya yüklememeyi tercih ederseniz bağlantı ekleyebilirsiniz. Bu bağlantı başka bir sayfanın veya bulut depolamadaki bir dosyanın bağlantısı olabilir.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Dosya bağlantısı',
     'attachments_link_url_hint' => 'Dosyanın veya sitenin url adresi',
     'attach' => 'Ekle',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Dosyayı Düzenle',
     'attachments_edit_file_name' => 'Dosya Adı',
     'attachments_edit_drop_upload' => 'Üzerine yazılacak dosyaları sürükleyin veya seçin',
index af2a47623475c5cf804358eec5608976149abe4b..d3a24622c2c8eb73bba91c3c5a4a97155aa27154 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Dosya yüklemesi zaman aşımına uğradı',
 
     // Attachments
-    'attachment_page_mismatch' => 'Ek güncellemesi sırasında sayfa uyuşmazlığı yaşandı',
     'attachment_not_found' => 'Ek bulunamadı',
 
     // Pages
index 545c91fe0c3b1046c028ff1d191f66edcedb2340..14da79438c0b6fabbbf9f7e998b744f1eec78c13 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'E-posta iletimi çalışıyor gibi görünüyor!',
     'maint_send_test_email_mail_text' => 'Tebrikler! Eğer bu e-posta bildirimini alıyorsanız, e-posta ayarlarınız doğru bir şekilde ayarlanmış demektir.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Roller',
     'role_user_roles' => 'Kullanıcı Rolleri',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Sistem programlama arayüzüne (API) eriş',
     'role_manage_settings' => 'Uygulama ayarlarını yönet',
     'role_asset' => 'Varlık Yetkileri',
+    'roles_system_warning' => 'Yukarıdaki üç izinden herhangi birine erişimin, kullanıcının kendi ayrıcalıklarını veya sistemdeki diğerlerinin ayrıcalıklarını değiştirmesine izin verebileceğini unutmayın. Yalnızca bu izinlere sahip rolleri güvenilir kullanıcılara atayın.',
     'role_asset_desc' => 'Bu izinler, sistem içindeki varlıklara varsayılan erişim izinlerini ayarlar. Kitaplar, bölümler ve sayfalar üzerindeki izinler, buradaki izinleri geçersiz kılar.',
     'role_asset_admins' => 'Yöneticilere otomatik olarak bütün içeriğe erişim yetkisi verilir ancak bu seçenekler, kullanıcı arayüzündeki bazı seçeneklerin gösterilmesine veya gizlenmesine neden olabilir.',
     'role_all' => 'Hepsi',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Danca',
         'de' => 'Deutsch (Sie)',
index 3b6e354c42dc7512b56e260ccc9c06e7f01ce24f..e11848a2092125e02ab979bc7e4de2fec4c47dc3 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => 'Скинути пароль',
     'reset_password_send_instructions' => 'Введіть адресу електронної пошти нижче, і вам буде надіслано електронне повідомлення з посиланням на зміну пароля.',
     'reset_password_send_button' => 'Надіслати посилання для скидання пароля',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => 'Посилання для скидання пароля буде надіслано на :email, якщо ця електронна адреса вказана в системі.',
     'reset_password_success' => 'Ваш пароль успішно скинуто.',
     'email_reset_subject' => 'Скинути ваш пароль :appName',
     'email_reset_text' => 'Ви отримали цей електронний лист, оскільки до нас надійшов запит на скидання пароля для вашого облікового запису.',
index 225429ffa1aa594b52ee0abcb9a2efc9d3d1ccf5..1af8502cd7f8d37919d4ec53b6f2352131ae0756 100644 (file)
@@ -33,12 +33,13 @@ return [
     'copy' => 'Копіювати',
     'reply' => 'Відповісти',
     'delete' => 'Видалити',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Шукати',
     'search_clear' => 'Очистити пошук',
     'reset' => 'Скинути',
     'remove' => 'Видалити',
     'add' => 'Додати',
-    'fullscreen' => 'Fullscreen',
+    'fullscreen' => 'На весь екран',
 
     // Sort Options
     'sort_options' => 'Параметри сортування',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => 'Меню профілю',
     'view_profile' => 'Переглянути профіль',
     'edit_profile' => 'Редагувати профіль',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => 'Темний режим',
+    'light_mode' => 'Світлий режим',
 
     // Layout tabs
     'tab_info' => 'Інфо',
index 0cd7e8804d55beda2ba7d7a79d68511ba2d30640..bf4622c2b33b621e50b6370f1687e2dbd230417e 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Завантажити ще',
     'image_image_name' => 'Назва зображення',
     'image_delete_used' => 'Це зображення використовується на наступних сторінках.',
-    'image_delete_confirm' => 'Натисніть кнопку Видалити ще раз, щоб підтвердити, що хочете видалити це зображення.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Вибрати зображення',
     'image_dropzone' => 'Перетягніть зображення, або натисніть тут для завантаження',
     'images_deleted' => 'Зображень видалено',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Редагувати код',
     'code_language' => 'Мова коду',
     'code_content' => 'Вміст коду',
+    'code_session_history' => 'Session History',
     'code_save' => 'Зберегти Код',
 ];
index 7f941718972c2c5a8b2abeb1f041e0cd4ce2cced..45da0e21cd0f6b4390a10ae734e111a210d78cc0 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Немає сторінок, які відповідають цьому пошуку',
     'search_for_term' => 'Шукати :term',
     'search_more' => 'Більше результатів',
-    'search_filters' => 'Фільтри пошуку',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Тип вмісту',
     'search_exact_matches' => 'Точна відповідність',
     'search_tags' => 'Пошукові теги',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Завантажити файл',
     'attachments_link' => 'Приєднати посилання',
     'attachments_set_link' => 'Встановити посилання',
-    'attachments_delete_confirm' => 'Натисніть кнопку Видалити ще раз, щоб підтвердити, що ви хочете видалити це вкладення.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Перетягніть файли, або натисніть тут щоб прикріпити файл',
     'attachments_no_files' => 'Файли не завантажені',
     'attachments_explain_link' => 'Ви можете приєднати посилання, якщо не бажаєте завантажувати файл. Це може бути посилання на іншу сторінку або посилання на файл у хмарі.',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Посилання на файл',
     'attachments_link_url_hint' => 'URL-адреса сайту або файлу',
     'attach' => 'Приєднати',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Редагувати файл',
     'attachments_edit_file_name' => 'Назва файлу',
     'attachments_edit_drop_upload' => 'Перетягніть файли, або натисніть тут щоб завантажити та перезаписати',
index f3aa299ed72ffcc103778a639ab3871e2c01c32c..bf9a46ecb2c21de2db3abd989f701df8d85dbc7a 100644 (file)
@@ -13,7 +13,7 @@ return [
     'email_already_confirmed' => 'Електронна пошта вже підтверджена, спробуйте увійти.',
     'email_confirmation_invalid' => 'Цей токен підтвердження недійсний або вже був використаний, будь ласка, спробуйте знову зареєструватися.',
     'email_confirmation_expired' => 'Термін дії токена підтвердження минув, новий електронний лист підтвердження був відправлений.',
-    'email_confirmation_awaiting' => 'The email address for the account in use needs to be confirmed',
+    'email_confirmation_awaiting' => 'Потрібно підтвердити адресу електронної пошти для облікового запису, який використовується',
     'ldap_fail_anonymous' => 'LDAP-доступ невдалий, з використання анонімного зв\'язку',
     'ldap_fail_authed' => 'LDAP-доступ невдалий, використовуючи задані параметри dn та password',
     'ldap_extension_not_installed' => 'Розширення PHP LDAP не встановлено',
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Тайм-аут при завантаженні файлу',
 
     // Attachments
-    'attachment_page_mismatch' => 'Невідповідність сторінки при оновленні вкладень',
     'attachment_not_found' => 'Вкладення не знайдено',
 
     // Pages
@@ -83,21 +82,21 @@ return [
     // Error pages
     '404_page_not_found' => 'Сторінку не знайдено',
     'sorry_page_not_found' => 'Вибачте, сторінку, яку ви шукали, не знайдено.',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => 'Якщо ви очікували що ця сторінки існує – можливо у вас немає дозволу на її перегляд.',
     'return_home' => 'Повернутися на головну',
     'error_occurred' => 'Виникла помилка',
     'app_down' => ':appName зараз недоступний',
     'back_soon' => 'Він повернеться найближчим часом.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
-    'api_bad_authorization_format' => 'An authorization token was found on the request but the format appeared incorrect',
-    'api_user_token_not_found' => 'No matching API token was found for the provided authorization token',
+    'api_no_authorization_found' => 'У запиті не знайдено токен авторизації',
+    'api_bad_authorization_format' => 'У запиті знайдено токен авторизації, але формат недійсний',
+    'api_user_token_not_found' => 'Не знайдено відповідного API-токена для наданого токена авторизації',
     'api_incorrect_token_secret' => 'The secret provided for the given used API token is incorrect',
     'api_user_no_api_permission' => 'The owner of the used API token does not have permission to make API calls',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_user_token_expired' => 'Термін дії токена авторизації закінчився',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => 'Помилка під час надсилання тестового електронного листа:',
 
 ];
index 250bec3fbe9907ce7188b8f7fc08bc8f5f5c6053..90c31777c9876fb8c1f3da3ada0fd54998256957 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => 'Пароль повинен містити не менше восьми символів і збігатись з підтвердженням.',
     'user' => "Ми не можемо знайти користувача з цією адресою електронної пошти.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Токен скидання пароля недійсний для цієї адреси електронної пошти.',
     'sent' => 'Ми надіслали Вам електронний лист із посиланням для скидання пароля!',
     'reset' => 'Ваш пароль скинуто!',
 
index d7dd8397bcefb064eea5836cd2b52276dd1a8279..1caf94701229cc30688f2e440ce59022cbc0a552 100644 (file)
@@ -43,10 +43,10 @@ return [
 
     // Color settings
     'content_colors' => 'Кольори вмісту',
-    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'content_colors_desc' => 'Встановлює кольори для всіх елементів в ієрархії організації сторінок. Рекомендуємо вибирати кольори із яскравістю, схожою на кольори за замовчуванням, для кращої читабельності.',
     'bookshelf_color' => 'Колір полиці',
     'book_color' => 'Колір книги',
-    'chapter_color' => 'Chapter Color',
+    'chapter_color' => 'Колір глави',
     'page_color' => 'Колір сторінки',
     'page_draft_color' => 'Колір чернетки',
 
@@ -56,7 +56,7 @@ return [
     'reg_enable_toggle' => 'Дозволити реєстрацію',
     'reg_enable_desc' => 'При включенні реєстрації відвідувач зможе зареєструватися як користувач програми. Після реєстрації їм надається єдина роль користувача за замовчуванням.',
     'reg_default_role' => 'Роль користувача за умовчанням після реєстрації',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
+    'reg_enable_external_warning' => 'Цей параметр ігнорується, якщо активна зовнішня автентифікація LDAP або SAML. Облікові записи користувачів для неіснуючих учасників будуть створені автоматично, якщо аутентифікація у зовнішній системі буде успішною.',
     'reg_email_confirmation' => 'Підтвердження електронною поштою',
     'reg_email_confirmation_toggle' => 'Необхідне підтвердження електронною поштою',
     'reg_confirm_email_desc' => 'Якщо використовується обмеження домену, то підтвердження електронною поштою буде потрібно, а нижче значення буде проігноровано.',
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Доставляння електронної пошти працює!',
     'maint_send_test_email_mail_text' => 'Вітаємо! Оскільки ви отримали цього листа, поштова скринька налаштована правильно.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Ролі',
     'role_user_roles' => 'Ролі користувача',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Access system API',
     'role_manage_settings' => 'Керування налаштуваннями програми',
     'role_asset' => 'Дозволи',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Ці дозволи контролюють стандартні доступи всередині системи. Права на книги, розділи та сторінки перевизначать ці дозволи.',
     'role_asset_admins' => 'Адміністратори автоматично отримують доступ до всього вмісту, але ці параметри можуть відображати або приховувати параметри інтерфейсу користувача.',
     'role_all' => 'Все',
@@ -164,7 +179,7 @@ return [
     'user_api_token_name_desc' => 'Give your token a readable name as a future reminder of its intended purpose.',
     'user_api_token_expiry' => 'Expiry Date',
     'user_api_token_expiry_desc' => 'Set a date at which this token expires. After this date, requests made using this token will no longer work. Leaving this field blank will set an expiry 100 years into the future.',
-    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID"" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
+    'user_api_token_create_secret_message' => 'Immediately after creating this token a "Token ID" & "Token Secret" will be generated and displayed. The secret will only be shown a single time so be sure to copy the value to somewhere safe and secure before proceeding.',
     'user_api_token_create_success' => 'API token successfully created',
     'user_api_token_update_success' => 'API token successfully updated',
     'user_api_token' => 'API Token',
@@ -172,8 +187,8 @@ return [
     'user_api_token_id_desc' => 'This is a non-editable system generated identifier for this token which will need to be provided in API requests.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'This is a system generated secret for this token which will need to be provided in API requests. This will only be displayed this one time so copy this value to somewhere safe and secure.',
-    'user_api_token_created' => 'Token Created :timeAgo',
-    'user_api_token_updated' => 'Token Updated :timeAgo',
+    'user_api_token_created' => 'Token created :timeAgo',
+    'user_api_token_updated' => 'Token updated :timeAgo',
     'user_api_token_delete' => 'Delete Token',
     'user_api_token_delete_warning' => 'This will fully delete this API token with the name \':tokenName\' from the system.',
     'user_api_token_delete_confirm' => 'Are you sure you want to delete this API token?',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
index 2b56b4639448fe29d45113f001208551e21cd01b..5106e276970b48fa6875f0c8931da8fa2fb1c20c 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => 'Sao chép',
     'reply' => 'Trả lời',
     'delete' => 'Xóa',
+    'delete_confirm' => 'Confirm Deletion',
     'search' => 'Tìm kiếm',
     'search_clear' => 'Xoá tìm kiếm',
     'reset' => 'Thiết lập lại',
index c1a9b343d3f4299f85fd3d9abe7c04bf1e6a10ac..cdcce776d07192f2a47865a44cd0ac88fe9b890c 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Hiện thêm',
     'image_image_name' => 'Tên Ảnh',
     'image_delete_used' => 'Ảnh này được sử dụng trong các trang dưới đây.',
-    'image_delete_confirm' => 'Bấm nút xóa lần nữa để xác nhận bạn muốn xóa ảnh này.',
+    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
     'image_select_image' => 'Chọn Ảnh',
     'image_dropzone' => 'Thả các ảnh hoặc bấm vào đây để tải lên',
     'images_deleted' => 'Các ảnh đã được xóa',
@@ -29,5 +29,6 @@ return [
     'code_editor' => 'Sửa Mã',
     'code_language' => 'Ngôn ngữ Mã',
     'code_content' => 'Nội dung Mã',
+    'code_session_history' => 'Session History',
     'code_save' => 'Lưu Mã',
 ];
index 31fbaaf7ff2acc622db97e94753d0398b5043d7e..be16dd72b92ae4361663b2a46c6dbf81e3eb0483 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => 'Không trang nào khớp với tìm kiếm này',
     'search_for_term' => 'Tìm kiếm cho :term',
     'search_more' => 'Thêm kết quả',
-    'search_filters' => 'Bộ lọc Tìm kiếm',
+    'search_advanced' => 'Advanced Search',
+    'search_terms' => 'Search Terms',
     'search_content_type' => 'Kiểu Nội dung',
     'search_exact_matches' => 'Hoàn toàn trùng khớp',
     'search_tags' => 'Tìm kiếm Tag',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => 'Tải lên Tập tin',
     'attachments_link' => 'Đính kèm Liên kết',
     'attachments_set_link' => 'Đặt Liên kết',
-    'attachments_delete_confirm' => 'Bấm xóa lần nữa để xác nhận bạn muốn xóa đính kèm này.',
+    'attachments_delete' => 'Are you sure you want to delete this attachment?',
     'attachments_dropzone' => 'Thả các tập tin hoặc bấm vào đây để đính kèm một tập tin',
     'attachments_no_files' => 'Không có tập tin nào được tải lên',
     'attachments_explain_link' => 'Bạn có thể đính kèm một liên kết nếu bạn lựa chọn không tải lên tập tin. Liên kết này có thể trỏ đến một trang khác hoặc một tập tin ở trên mạng (đám mây).',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => 'Liên kết đến tập tin',
     'attachments_link_url_hint' => 'URL của trang hoặc tập tin',
     'attach' => 'Đính kèm',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => 'Sửa tập tin',
     'attachments_edit_file_name' => 'Tên tệp tin',
     'attachments_edit_drop_upload' => 'Thả tập tin hoặc bấm vào đây để tải lên và ghi đè',
index e93f74e64ee37f1db1716328c70981e64b355fc2..6caf0e9904ef244cd6f536b9ec29f3268660f391 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => 'Đã quá thời gian tải lên tệp tin.',
 
     // Attachments
-    'attachment_page_mismatch' => 'Trang không trùng khớp khi cập nhật đính kèm',
     'attachment_not_found' => 'Không tìm thấy đính kèm',
 
     // Pages
index 4f7469228626d2e8f3ae3abd6d65c2fa5a5e54ad..5d35ff4399cf10c1748c17d8aa1c4c90a85db200 100644 (file)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => 'Chức năng gửi email có vẻ đã hoạt động!',
     'maint_send_test_email_mail_text' => 'Chúc mừng! Khi bạn nhận được email thông báo này, cài đặt email của bạn có vẻ đã được cấu hình đúng.',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => 'Quyền',
     'role_user_roles' => 'Quyền người dùng',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => 'Truy cập đến API hệ thống',
     'role_manage_settings' => 'Quản lý cài đặt của ứng dụng',
     'role_asset' => 'Quyền tài sản (asset)',
+    'roles_system_warning' => 'Be aware that access to any of the above three permissions can allow a user to alter their own privileges or the privileges of others in the system. Only assign roles with these permissions to trusted users.',
     'role_asset_desc' => 'Các quyền này điều khiển truy cập mặc định tới tài sản (asset) nằm trong hệ thống. Quyền tại Sách, Chường và Trang se ghi đè các quyền này.',
     'role_asset_admins' => 'Quản trị viên được tự động cấp quyền truy cập đến toàn bộ nội dung, tuy nhiên các tùy chọn đó có thể hiện hoặc ẩn tùy chọn giao diện.',
     'role_all' => 'Tất cả',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => 'Đan Mạch',
         'de' => 'Deutsch (Sie)',
index e1d25078db7bdedc75960cf38b015fb61002d57a..a7f8017cb68e81bf4b6b2a05c0c85414a1580e6f 100644 (file)
@@ -43,7 +43,7 @@ return [
     'reset_password' => '重置密码',
     'reset_password_send_instructions' => '在下面输入您的Email地址,您将收到一封带有密码重置链接的邮件。',
     'reset_password_send_button' => '发送重置链接',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => '重置密码的链接将通过您的电子邮箱发送:email。',
     'reset_password_success' => '您的密码已成功重置。',
     'email_reset_subject' => '重置您的:appName密码',
     'email_reset_text' => '您收到此电子邮件是因为我们收到了您的帐户的密码重置请求。',
index bc9e94d18057f41775a1b25fcd654b50c63cb319..e96edaf1e990881438461ac00ec051a612e898eb 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => '复制',
     'reply' => '回复',
     'delete' => '删除',
+    'delete_confirm' => '确认删除',
     'search' => '搜索',
     'search_clear' => '清除搜索',
     'reset' => '重置',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => '个人资料',
     'view_profile' => '查看资料',
     'edit_profile' => '编辑资料',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => '夜间模式',
+    'light_mode' => '日间模式',
 
     // Layout tabs
     'tab_info' => '信息',
index 54d0fb085731550136fa218feb6628def6d11e5a..ab0b7cb469ec5a25179e1b26902cf47fd2401aa2 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => '显示更多',
     'image_image_name' => '图片名称',
     'image_delete_used' => '该图像用于以下页面。',
-    'image_delete_confirm' => '如果你想删除它,请再次按下按钮。',
+    'image_delete_confirm_text' => '您确认要删除此图片吗?',
     'image_select_image' => '选择图片',
     'image_dropzone' => '拖放图片或点击此处上传',
     'images_deleted' => '图片已删除',
@@ -29,5 +29,6 @@ return [
     'code_editor' => '编辑代码',
     'code_language' => '编程语言',
     'code_content' => '代码内容',
+    'code_session_history' => '会话历史',
     'code_save' => '保存代码',
 ];
index 5c614b079b07f036bc357c2ffe57329176a277dc..babb9f28266fcc4f6e23ed32ebe719fbde330f82 100644 (file)
@@ -47,7 +47,8 @@ return [
     'search_no_pages' => '没有找到相匹配的页面',
     'search_for_term' => '“:term”的搜索结果',
     'search_more' => '更多结果',
-    'search_filters' => '过滤搜索结果',
+    'search_advanced' => '高级搜索',
+    'search_terms' => '搜索关键词',
     'search_content_type' => '种类',
     'search_exact_matches' => '精确匹配',
     'search_tags' => '标签搜索',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => '上传文件',
     'attachments_link' => '附加链接',
     'attachments_set_link' => '设置链接',
-    'attachments_delete_confirm' => '确认您想要删除此附件后,请点击删除。',
+    'attachments_delete' => '您确定要删除此附件吗?',
     'attachments_dropzone' => '删除文件或点击此处添加文件',
     'attachments_no_files' => '尚未上传文件',
     'attachments_explain_link' => '如果您不想上传文件,则可以附加链接,这可以是指向其他页面的链接,也可以是指向云端文件的链接。',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => '链接到文件',
     'attachments_link_url_hint' => '网站或文件的网址',
     'attach' => '附加',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => '编辑文件',
     'attachments_edit_file_name' => '文件名',
     'attachments_edit_drop_upload' => '删除文件或点击这里上传并覆盖',
index 21034f3991359f681c6a4cb13c87e9b50476ac61..b41e21ac3e83b9858edece28d99428dfa581f2b8 100644 (file)
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => '文件上传已超时。',
 
     // Attachments
-    'attachment_page_mismatch' => '附件更新期间的页面不匹配',
     'attachment_not_found' => '找不到附件',
 
     // Pages
@@ -83,7 +82,7 @@ return [
     // Error pages
     '404_page_not_found' => '无法找到页面',
     'sorry_page_not_found' => '对不起,无法找到您想访问的页面。',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => '您可能没有查看权限。',
     'return_home' => '返回主页',
     'error_occurred' => '出现错误',
     'app_down' => ':appName现在正在关闭',
index ac3f5e2ae3c5218a9929be45b03f32f3ef434ca7..8d8272ee01fd9f774e2b30c4cf2a5f35a2677405 100644 (file)
@@ -8,7 +8,7 @@ return [
 
     'password' => '密码必须至少包含六个字符并与确认相符。',
     'user' => "使用该Email地址的用户不存在。",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => '重置密码链接无法发送至此邮件地址。',
     'sent' => '我们已经通过Email发送您的密码重置链接!',
     'reset' => '您的密码已被重置!',
 
index c2f0155330e1e86a4544929dd1707eae991c1203..534b5ffa9a65008be9f09ce3588358910fda153a 100755 (executable)
@@ -81,6 +81,20 @@ return [
     'maint_send_test_email_mail_greeting' => '邮件发送功能看起来工作正常!',
     'maint_send_test_email_mail_text' => '恭喜!您收到了此邮件通知,你的电子邮件设置看起来配置正确。',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => '角色',
     'role_user_roles' => '用户角色',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => '访问系统 API',
     'role_manage_settings' => '管理App设置',
     'role_asset' => '资源许可',
+    'roles_system_warning' => '请注意,具有上述三个权限中的任何一个都可以允许用户更改自己的特权或系统中其他人的特权。 只将具有这些权限的角色分配给受信任的用户。',
     'role_asset_desc' => '对系统内资源的默认访问许可将由这些权限控制。单独设置在书籍,章节和页面上的权限将覆盖这里的权限设定。',
     'role_asset_admins' => '管理员可自动获得对所有内容的访问权限,但这些选项可能会显示或隐藏UI选项。',
     'role_all' => '全部的',
@@ -131,7 +146,7 @@ return [
     'users_send_invite_text' => '您可以向该用户发送邀请电子邮件,允许他们设置自己的密码,否则,您可以自己设置他们的密码。',
     'users_send_invite_option' => '发送邀请用户电子邮件',
     'users_external_auth_id' => '外部身份认证ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_external_auth_id_desc' => '这是用于与您的外部身份验证系统通信时匹配此用户的ID。',
     'users_password_warning' => '如果您想更改密码,请填写以下内容:',
     'users_system_public' => '此用户代表访问您的App的任何访客。它不能用于登录,而是自动分配。',
     'users_delete' => '删除用户',
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => '丹麦',
         'de' => 'Deutsch (Sie)',
index 5cf2bd3cf01c6ccd7d1e1994049b3085a7c661e3..fab4dd20e62b2706793c643f335e8f99c5d630fd 100644 (file)
@@ -6,41 +6,41 @@
 return [
 
     // Pages
-    'page_create'                 => '建ç«\8bäº\86頁面',
+    'page_create'                 => '已建ç«\8b頁面',
     'page_create_notification'    => '頁面已建立成功',
-    'page_update'                 => '更新了頁面',
+    'page_update'                 => '已更新頁面',
     'page_update_notification'    => '頁面已更新成功',
-    'page_delete'                 => 'å\88ªé\99¤äº\86頁面',
+    'page_delete'                 => 'å·²å\88ªé\99¤頁面',
     'page_delete_notification'    => '頁面已刪除成功',
-    'page_restore'                => '恢複了頁面',
-    'page_restore_notification'   => '頁面已恢複成功',
+    'page_restore'                => '已還原頁面',
+    'page_restore_notification'   => '頁面已還原成功',
     'page_move'                   => '移動了頁面',
 
     // Chapters
-    'chapter_create'              => '建ç«\8bäº\86章節',
+    'chapter_create'              => '已建ç«\8b章節',
     'chapter_create_notification' => '章節已建立成功',
-    'chapter_update'              => '更新了章節',
+    'chapter_update'              => '已更新章節',
     'chapter_update_notification' => '章節已建立成功',
-    'chapter_delete'              => 'å\88ªé\99¤äº\86章節',
+    'chapter_delete'              => 'å·²å\88ªé\99¤章節',
     'chapter_delete_notification' => '章節已刪除成功',
-    'chapter_move'                => '移動了章節',
+    'chapter_move'                => '已移動章節',
 
     // Books
-    'book_create'                 => '建ç«\8bäº\86å\9c\96æ\9b¸',
-    'book_create_notification'    => '圖書已建立成功',
+    'book_create'                 => '已建ç«\8bæ\9b¸æ\9c¬',
+    'book_create_notification'    => '書本已建立成功',
     'book_update'                 => '更新了圖書',
-    'book_update_notification'    => '圖書已更新成功',
-    'book_delete'                 => 'å\88ªé\99¤äº\86å\9c\96æ\9b¸',
-    'book_delete_notification'    => '圖書已刪除成功',
-    'book_sort'                   => '排序了圖書',
-    'book_sort_notification'      => '圖書已重新排序成功',
+    'book_update_notification'    => '書本已更新成功',
+    'book_delete'                 => 'å·²å\88ªé\99¤æ\9b¸æ\9c¬',
+    'book_delete_notification'    => '書本已刪除成功',
+    'book_sort'                   => '已排序書本',
+    'book_sort_notification'      => '書本已重新排序成功',
 
     // Bookshelves
-    'bookshelf_create'            => '建ç«\8bäº\86書架',
+    'bookshelf_create'            => '已建ç«\8b書架',
     'bookshelf_create_notification'    => '書架已建立成功',
-    'bookshelf_update'                 => '更新了書架',
+    'bookshelf_update'                 => '已更新書架',
     'bookshelf_update_notification'    => '書架已更新成功',
-    'bookshelf_delete'                 => 'å\88ªé\99¤äº\86書架',
+    'bookshelf_delete'                 => 'å·²å\88ªé\99¤書架',
     'bookshelf_delete_notification'    => '書架已刪除成功',
 
     // Other
index c72e5d206e7813c6cd471f3e16c9f98f7eb61a92..2ad648589af9cd61cb95c88e3a9ff76d0a138e73 100644 (file)
@@ -7,7 +7,7 @@
 return [
 
     'failed' => '使用者名稱或密碼錯誤。',
-    'throttle' => '您的登入次數過多,請在:秒後重試。',
+    'throttle' => '您的登入次數過多,請在:seconds秒後重試。',
 
     // Login & Register
     'sign_up' => '註冊',
@@ -18,32 +18,32 @@ return [
 
     'name' => '名稱',
     'username' => '使用者名稱',
-    'email' => 'Email位址',
+    'email' => '電子郵件',
     'password' => '密碼',
     'password_confirm' => '確認密碼',
-    'password_hint' => '必須超過7個字元',
+    'password_hint' => '必須超過 7 個字元',
     'forgot_password' => '忘記密碼?',
-    'remember_me' => '記住該賬戶密碼',
-    'ldap_email_hint' => '請輸入用於此帳號的電子郵件。',
-    'create_account' => '建立帳',
-    'already_have_account' => '已經擁有戶?',
-    'dont_have_account' => '沒有戶?',
-    'social_login' => 'SNS登入',
-    'social_registration' => 'SNS註冊',
-    'social_registration_text' => '其他服務註冊/登入.',
+    'remember_me' => '記住',
+    'ldap_email_hint' => '請輸入此帳號使用的電子郵件。',
+    'create_account' => '建立帳',
+    'already_have_account' => '已經擁有戶?',
+    'dont_have_account' => '沒有戶?',
+    'social_login' => '社群網站登入',
+    'social_registration' => '社群網站帳戶註冊',
+    'social_registration_text' => '使用其他服務註冊及登入。',
 
-    'register_thanks' => '註冊完成!',
-    'register_confirm' => '請點選查收您的Email,並點選確認。',
-    'registrations_disabled' => '註冊目前被禁用',
-    'registration_email_domain_invalid' => '此Email域名沒有權限進入本系統',
+    'register_thanks' => '感謝您的註冊!',
+    'register_confirm' => '請檢查您的電子郵件,並按下確認按鈕以使用 :appName 。',
+    'registrations_disabled' => '目前已停用註冊',
+    'registration_email_domain_invalid' => '這個電子郵件網域沒有權限使用',
     'register_success' => '感謝您註冊:appName,您現在已經登入。',
 
 
     // Password Reset
     'reset_password' => '重置密碼',
-    'reset_password_send_instructions' => '在下方輸入您的Email位址,您將收到一封帶有密碼重置連結的郵件。',
+    'reset_password_send_instructions' => '在下方輸入您的電子郵件,您將收到一封帶有密碼重置連結的郵件。',
     'reset_password_send_button' => '發送重置連結',
-    'reset_password_sent' => 'A password reset link will be sent to :email if that email address is found in the system.',
+    'reset_password_sent' => '重置密碼的連結會發送至電子郵件地址:email(如果系統記錄中存在此電子郵件地址)',
     'reset_password_success' => '您的密碼已成功重置。',
     'email_reset_subject' => '重置您的:appName密碼',
     'email_reset_text' => '您收到此電子郵件是因為我們收到了您的帳號的密碼重置請求。',
@@ -51,7 +51,7 @@ return [
 
 
     // Email Confirmation
-    'email_confirm_subject' => '確認您在:appName的Email位址',
+    'email_confirm_subject' => '確認您在:appName的電子郵件',
     'email_confirm_greeting' => '感謝您加入:appName!',
     'email_confirm_text' => '請點選下面的按鈕確認您的Email位址:',
     'email_confirm_action' => '確認Email',
@@ -66,12 +66,12 @@ return [
     'email_not_confirmed_resend_button' => '重新發送確認Email',
 
     // User Invite
-    'user_invite_email_subject' => '您被邀請加入:bookstack!',
-    'user_invite_email_greeting' => '我們為您在bookstack上創建了一個新賬戶。',
+    'user_invite_email_subject' => '您受邀請加入:appName!',
+    'user_invite_email_greeting' => '我們為您在:appName上創建了一個新賬戶。',
     'user_invite_email_text' => '請點擊下面的按鈕設置賬戶密碼并獲取訪問權限:',
     'user_invite_email_action' => '請設置賬戶密碼',
-    'user_invite_page_welcome' => '歡迎使用:bookstack',
-    'user_invite_page_text' => '要完善您的賬戶并獲取訪問權限,您需要設置一個密碼,該密碼將在以後訪問時用於登陸:bookstack',
+    'user_invite_page_welcome' => '歡迎使用:appName',
+    'user_invite_page_text' => '要完成設置您的賬戶並獲取訪問權限,您需要設置一個密碼。該密碼將在以後訪問時用於登陸:appName',
     'user_invite_page_confirm_button' => '請確定密碼',
-    'user_invite_success' => '密碼已設置,您現在可以進入:bookstack了啦'
+    'user_invite_success' => '密碼已設置,您現在可以進入:appName了啦!'
 ];
\ No newline at end of file
index c86b0940c57cd169581b5f35be8d04979f227e40..1d26ded298f6ca1ed15435438d76b843ce066225 100644 (file)
@@ -33,6 +33,7 @@ return [
     'copy' => '複製',
     'reply' => '回覆',
     'delete' => '刪除',
+    'delete_confirm' => '確認刪除',
     'search' => '搜尋',
     'search_clear' => '清除搜尋',
     'reset' => '重置',
@@ -66,8 +67,8 @@ return [
     'profile_menu' => '個人資料菜單',
     'view_profile' => '檢視資料',
     'edit_profile' => '編輯資料',
-    'dark_mode' => 'Dark Mode',
-    'light_mode' => 'Light Mode',
+    'dark_mode' => '深色模式',
+    'light_mode' => '明亮模式',
 
     // Layout tabs
     'tab_info' => '訊息',
index bcadecbb6bafb1788ec2f2898afec2ace7a37b89..b200fa7948506c3d56e5e653642b2c38a690f8ca 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => '載入更多',
     'image_image_name' => '圖片名稱',
     'image_delete_used' => '所使用圖片目前用於以下頁面。',
-    'image_delete_confirm' => '如果你想刪除它,請再次按下按鈕。',
+    'image_delete_confirm_text' => '您確認想要刪除這個圖片?',
     'image_select_image' => '選擇圖片',
     'image_dropzone' => '拖曳圖片或點選這裡上傳',
     'images_deleted' => '圖片已刪除',
@@ -29,5 +29,6 @@ return [
     'code_editor' => '編輯程式碼',
     'code_language' => '程式語言',
     'code_content' => '程式碼內容',
+    'code_session_history' => 'Session 歷程',
     'code_save' => '儲存程式碼',
 ];
index d3280217ce3e4de33168c54c9aecf38cf382ac91..bc08ee32a8be82573bfc91baf10fe9fabcef9acd 100644 (file)
@@ -42,12 +42,13 @@ return [
 
     // Search
     'search_results' => '搜尋結果',
-    'search_total_results_found' => '共找到了:count個結果',
+    'search_total_results_found' => '共找到了:count個結果|共找到了:count個結果',
     'search_clear' => '清除搜尋',
     'search_no_pages' => '沒有找到符合的頁面',
     'search_for_term' => '“:term”的搜尋結果',
     'search_more' => '更多結果',
-    'search_filters' => '過濾搜尋結果',
+    'search_advanced' => '進階搜尋',
+    'search_terms' => '搜尋字串',
     'search_content_type' => '種類',
     'search_exact_matches' => '精確符合',
     'search_tags' => '標籤搜尋',
@@ -68,7 +69,7 @@ return [
     // Shelves
     'shelf' => '書架',
     'shelves' => '書架',
-    'x_shelves' => ':架|:章節',
+    'x_shelves' => ':count 書架|:count 章節',
     'shelves_long' => '書架',
     'shelves_empty' => '不存在已建立的書架',
     'shelves_create' => '建立書架',
@@ -100,7 +101,7 @@ return [
     // Books
     'book' => '書本',
     'books' => '書本',
-    'x_books' => ':count本書',
+    'x_books' => ':count本書|:count本書',
     'books_empty' => '不存在已建立的書',
     'books_popular' => '熱門書本',
     'books_recent' => '最近的書',
@@ -139,7 +140,7 @@ return [
     // Chapters
     'chapter' => '章節',
     'chapters' => '章節',
-    'x_chapters' => ':count個章節',
+    'x_chapters' => ':count個章節|:count個章節',
     'chapters_popular' => '熱門章節',
     'chapters_new' => '新章節',
     'chapters_create' => '建立章節',
@@ -162,7 +163,7 @@ return [
     // Pages
     'page' => '頁面',
     'pages' => '頁面',
-    'x_pages' => ':count個頁面',
+    'x_pages' => ':count個頁面|:count個頁面',
     'pages_popular' => '熱門頁面',
     'pages_new' => '新頁面',
     'pages_attachments' => '附件',
@@ -255,7 +256,7 @@ return [
     'attachments_upload' => '上傳檔案',
     'attachments_link' => '附加連結',
     'attachments_set_link' => '設定連結',
-    'attachments_delete_confirm' => '確認您想要刪除此附件後,請點選刪除。',
+    'attachments_delete' => '確定要刪除此附件嗎?',
     'attachments_dropzone' => '刪除檔案或點選此處加入檔案',
     'attachments_no_files' => '尚未上傳檔案',
     'attachments_explain_link' => '如果您不想上傳檔案,則可以附加連結,這可以是指向其他頁面的連結,也可以是指向雲端檔案的連結。',
@@ -264,6 +265,7 @@ return [
     'attachments_link_url' => '連結到檔案',
     'attachments_link_url_hint' => '網站或檔案的網址',
     'attach' => '附加',
+    'attachments_insert_link' => 'Add Attachment Link to Page',
     'attachments_edit_file' => '編輯檔案',
     'attachments_edit_file_name' => '檔案名稱',
     'attachments_edit_drop_upload' => '刪除檔案或點選這裡上傳並覆蓋',
@@ -286,20 +288,20 @@ return [
     'profile_not_created_pages' => ':userName尚未建立任何頁面',
     'profile_not_created_chapters' => ':userName尚未建立任何章節',
     'profile_not_created_books' => ':userName尚未建立任何書本',
-    'profile_not_created_shelves' => ':用戶名 沒有創建任何書架',
+    'profile_not_created_shelves' => ':userName 沒有創建任何書架',
 
     // Comments
     'comment' => '評論',
     'comments' => '評論',
     'comment_add' => '新增評論',
     'comment_placeholder' => '在這裡評論',
-    'comment_count' => '{0} 無評論|[1,*] :count條評論',
+    'comment_count' => '{0} 無評論|{1} :count條評論|[2,*] :count條評論',
     'comment_save' => '儲存評論',
     'comment_saving' => '正在儲存評論...',
     'comment_deleting' => '正在刪除評論...',
     'comment_new' => '新評論',
     'comment_created' => '評論於 :createDiff',
-    'comment_updated' => '更新於 :updateDiff (:username)',
+    'comment_updated' => '由 :username 於 :updateDiff 更新',
     'comment_deleted_success' => '評論已刪除',
     'comment_created_success' => '評論已加入',
     'comment_updated_success' => '評論已更新',
index f44f7eb8be52031867aada69174e08f35af40023..8604cc6c2690f24f8e62093adedd79f31926faa1 100644 (file)
@@ -22,14 +22,14 @@ return [
     'saml_user_not_registered' => '用戶:name未註冊,自動註冊不可用',
     'saml_no_email_address' => '在外部認證系統提供的數據中找不到該用戶的電子郵件地址',
     'saml_invalid_response_id' => '該應用程序啟動的進程無法識別來自外部身份驗證系統的請求。 登錄後返回可能會導致此問題。',
-    'saml_fail_authed' => '使用:system登錄失敗,系統未提供成功的授權',
+    'saml_fail_authed' => '使用 :system 登錄失敗,系統未提供成功的授權',
     'social_no_action_defined' => '沒有定義行為',
     'social_login_bad_response' => "在 :socialAccount 登錄時遇到錯誤:\n:error",
     'social_account_in_use' => ':socialAccount 帳號已被使用,請嘗試透過 :socialAccount 選項登錄。',
     'social_account_email_in_use' => 'Email :email 已經被使用。如果您已有帳號,則可以在個人資料設定中綁定您的 :socialAccount。',
     'social_account_existing' => ':socialAccount已經被綁定到您的帳號。',
     'social_account_already_used_existing' => ':socialAccount帳號已經被其他使用者使用。',
-    'social_account_not_used' => ':socialAccount帳號沒有綁定到任何使用者,請在您的個人資料設定中綁定。',
+    'social_account_not_used' => ':socialAccount帳號沒有綁定到任何使用者,請在您的個人資料設定中綁定。 ',
     'social_account_register_instructions' => '如果您還沒有帳號,您可以使用 :socialAccount 選項註冊帳號。',
     'social_driver_not_found' => '未找到社交驅動程式',
     'social_driver_not_configured' => '您的:socialAccount社交設定不正確。',
@@ -46,7 +46,6 @@ return [
     'file_upload_timeout' => '文件上傳已超時。',
 
     // Attachments
-    'attachment_page_mismatch' => '附件更新期間的頁面不符合',
     'attachment_not_found' => '沒有找到附件',
 
     // Pages
@@ -83,7 +82,7 @@ return [
     // Error pages
     '404_page_not_found' => '無法找到頁面',
     'sorry_page_not_found' => '對不起,無法找到您想進入的頁面。',
-    'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'sorry_page_not_found_permission_warning' => '如果您確認這個頁面存在,則代表可能沒有查看它的權限。',
     'return_home' => '返回首頁',
     'error_occurred' => '發生錯誤',
     'app_down' => ':appName現在正在關閉',
@@ -98,6 +97,6 @@ return [
     'api_user_token_expired' => '授權令牌已過期',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => '寄送測試電子郵件時發生錯誤:',
 
 ];
index 7d93b302beeda55ead01feb526401506bd199152..7040cd89095879af50403ac2be3c886a3074f364 100644 (file)
@@ -7,9 +7,9 @@
 return [
 
     'password' => '密碼必須至少包含六個字元並與確認相符。',
-    'user' => "使用該Email位址的使用者不存在。",
-    'token' => 'The password reset token is invalid for this email address.',
-    'sent' => '我們已經透過Email發送您的密碼重置連結。',
+    'user' => "沒有使用這個電子郵件位址的使用者。",
+    'token' => '這個電子郵件位址的密碼重置權仗無效。',
+    'sent' => '我們已經透過電子郵件發送您的密碼重置連結。',
     'reset' => '您的密碼已被重置。',
 
 ];
index f43e4204fe386c4b5691dc92bf07fcf112d0c4aa..b135cb08623dc876be645dba91bb4dc4bd4e10bc 100644 (file)
@@ -44,22 +44,22 @@ return [
     // Color settings
     'content_colors' => '內容顏色',
     'content_colors_desc' => '為頁面組織層次結構中的所有元素設置顏色。 為了提高可讀性,建議選擇亮度與默認顏色相似的顏色。',
-    'bookshelf_color' => '书架顏色',
-    'book_color' => '书本颜色',
-    'chapter_color' => '章节颜色',
-    'page_color' => '页é\9d¢é¢\9c色',
+    'bookshelf_color' => '書架顔色',
+    'book_color' => '書本顔色',
+    'chapter_color' => '章節顔色',
+    'page_color' => 'é \81é\9d¢é¡\94色',
     'page_draft_color' => '頁面草稿顏色',
 
     // Registration Settings
     'reg_settings' => '註冊設定',
     'reg_enable' => '啟用註冊',
     'reg_enable_toggle' => '啟用註冊',
-    'reg_enable_desc' => '啟用註冊後,用戶將可以自己註冊為應用程序用戶註冊後,他們將獲得一個默認的單一用戶角色。',
+    'reg_enable_desc' => '啟用註冊後,用戶將可以自己註冊為應用程序用戶註冊後,他們將獲得一個默認的單一用戶角色。',
     'reg_default_role' => '註冊後的預設使用者角色',
-    'reg_enable_external_warning' => 'The option above is ignored while external LDAP or SAML authentication is active. User accounts for non-existing members will be auto-created if authentication, against the external system in use, is successful.',
-    'reg_email_confirmation' => '电子邮箱认证',
-    'reg_email_confirmation_toggle' => '需要電子郵件確認',
-    'reg_confirm_email_desc' => '如果使用網域名稱限制,則需要Email驗證,並且本設定將被忽略。',
+    'reg_enable_external_warning' => '當外部 LDAP 或 SAML 身份驗證啟用時,將會忽略上述選項。如果外部身份驗證成功,將會自動在本系統建立使用者帳戶。',
+    'reg_email_confirmation' => '電子郵件驗證',
+    'reg_email_confirmation_toggle' => '需要電子郵件驗證',
+    'reg_confirm_email_desc' => '如果使用網域名稱限制,則需要電子郵件驗證,並且本設定將被忽略。',
     'reg_confirm_restrict_domain' => '網域名稱限制',
     'reg_confirm_restrict_domain_desc' => '輸入您想要限制註冊的Email域域名稱列表,用逗號隔開。在被允許與本系統連結之前,使用者會收到一封Email來確認他們的位址。<br>注意,使用者在註冊成功後可以修改他們的Email位址。',
     'reg_confirm_restrict_domain_placeholder' => '尚未設定限制的網域',
@@ -70,17 +70,31 @@ return [
     'maint_image_cleanup_desc' => "掃描頁面和修訂內容以檢查哪些圖像是正在使用的以及哪些圖像是多余的。確保在運行前創建完整的數據庫和映像備份。",
     'maint_image_cleanup_ignore_revisions' => '忽略修訂記錄中的圖像',
     'maint_image_cleanup_run' => '運行清理',
-    'maint_image_cleanup_warning' => '發現了 :count 張可能未使用的圖像。您確定要刪除這些圖像嗎?',
-    'maint_image_cleanup_success' => '找到並刪除了 :count 張可能未使用的圖像!',
-    'maint_image_cleanup_nothing_found' => 'æ\89¾ä¸\8då\88°æ\9cªä½¿ç\94¨ç\9a\84å\9c\96å\83\8fï¼\8cæ²\92æ\9c\89å\88ªé\99¤!',
+    'maint_image_cleanup_warning' => '發現了:count 張可能未使用的圖像。您確定要刪除這些圖像嗎?',
+    'maint_image_cleanup_success' => '找到並刪除了:count 張可能未使用的圖像!',
+    'maint_image_cleanup_nothing_found' => 'æ\89¾ä¸\8då\88°æ\9cªä½¿ç\94¨ç\9a\84å\9c\96å\83\8fï¼\8cæ\9cªå\88ªé\99¤ä»»ä½\95æª\94æ¡\88!',
     'maint_send_test_email' => '發送測試電子郵件',
     'maint_send_test_email_desc' => '這會將測試電子郵件發送到您的個人資料中指定的電子郵件地址。',
     'maint_send_test_email_run' => '發送測試郵件',
-    'maint_send_test_email_success' => '郵件發送到:地址',
+    'maint_send_test_email_success' => '郵件發送到 :address',
     'maint_send_test_email_mail_subject' => '測試郵件',
     'maint_send_test_email_mail_greeting' => '電子郵件傳遞似乎有效!',
     'maint_send_test_email_mail_text' => '恭喜你! 收到此電子郵件通知時,您的電子郵件設置已經認證成功。',
 
+    // Audit Log
+    'audit' => 'Audit Log',
+    'audit_desc' => 'This audit log displays a list of activities tracked in the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
+    'audit_event_filter' => 'Event Filter',
+    'audit_event_filter_no_filter' => 'No Filter',
+    'audit_deleted_item' => 'Deleted Item',
+    'audit_deleted_item_name' => 'Name: :name',
+    'audit_table_user' => 'User',
+    'audit_table_event' => 'Event',
+    'audit_table_item' => 'Related Item',
+    'audit_table_date' => 'Activity Date',
+    'audit_date_from' => 'Date Range From',
+    'audit_date_to' => 'Date Range To',
+
     // Role Settings
     'roles' => '角色',
     'role_user_roles' => '使用者角色',
@@ -106,6 +120,7 @@ return [
     'role_access_api' => '存取系統API',
     'role_manage_settings' => '管理App設定',
     'role_asset' => '資源項目',
+    'roles_system_warning' => '請注意,具有上述三項權限任何一項的用戶能更改自己或系統中其他人的權限。應只將具這些權限的角色分配予受信任的用戶。',
     'role_asset_desc' => '對系統內資源的預設權限將由這裡的權限控制。若有單獨設定在書本、章節和頁面上的權限,將會覆蓋這裡的權限設定。',
     'role_asset_admins' => '管理員會自動獲得對所有內容的存取權限,但這些選項可能會顯示或隱藏UI的選項。',
     'role_all' => '全部',
@@ -131,7 +146,7 @@ return [
     'users_send_invite_text' => '您可以選擇向該用戶發送邀請電子郵件,允許他們設置自己的密碼,或者您可以自己設置他們的密碼。',
     'users_send_invite_option' => '向用戶發送邀請電子郵件',
     'users_external_auth_id' => '外部身份驗證ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your external authentication system.',
+    'users_external_auth_id_desc' => '這個 ID 將於外部身份驗證系統時,用於比對使用者。',
     'users_password_warning' => '如果您想更改密碼,請填寫以下內容:',
     'users_system_public' => '此使用者代表進入您的App的任何訪客。它不能用於登入,而是自動分配。',
     'users_delete' => '刪除使用者',
@@ -172,10 +187,10 @@ return [
     'user_api_token_id_desc' => '這是此令牌的不可編輯的系統生成的標識符,需要在API請求中提供。',
     'user_api_token_secret' => '令牌密鑰',
     'user_api_token_secret_desc' => '這是此令牌的系統生成的密鑰,需要在API請求中提供。 這只會顯示一次,因此請將其複製到安全的地方。',
-    'user_api_token_created' => '令牌已創建:time Ago',
+    'user_api_token_created' => '令牌已創建於 :timeAgo',
     'user_api_token_updated' => '令牌已更新:timeAgo',
     'user_api_token_delete' => '刪除令牌',
-    'user_api_token_delete_warning' => '這將從系統中完全刪除名稱為\':tokenName\'的API令牌。',
+    'user_api_token_delete_warning' => '這將從系統中完全刪除名稱為 ":tokenName" 的API令牌。',
     'user_api_token_delete_confirm' => '您確定要刪除這個API令牌嗎?',
     'user_api_token_delete_success' => 'API令牌成功刪除',
 
@@ -185,6 +200,7 @@ return [
     'language_select' => [
         'en' => 'English',
         'ar' => 'العربية',
+        'bg' => 'Bǎlgarski',
         'cs' => 'Česky',
         'da' => '丹麥',
         'de' => 'Deutsch (Sie)',
@@ -192,7 +208,7 @@ return [
         'es' => 'Español',
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
-        'he' => 'עברית',
+        'he' => '希伯來語',
         'hu' => 'Magyar',
         'it' => 'Italian',
         'ja' => '日本語',
index 046e244514260413df7a910cb24a454ecd32e323..82d74e525a506d096d7682b900d6c41c3cd83b35 100644 (file)
@@ -30,19 +30,19 @@ return [
     'digits'               => ':attribute 必須為:digits位數。',
     'digits_between'       => ':attribute 必須為:min到:max位數。',
     'email'                => ':attribute 必須是有效的電子郵件位址。',
-    'ends_with' => ':attribute必須以下列之一結尾::values',
+    'ends_with' => ':attribute必須以下列之一結尾::values',
     'filled'               => ':attribute 字段是必需的。',
     'gt'                   => [
-        'numeric' => ':attribute必須大於:value。',
+        'numeric' => ':attribute必須大於:value。',
         'file'    => ':attribute必須大於:value千字節。',
-        'string'  => ':attribute必須大於:value字符。',
-        'array'   => ':attribute必須包含比:value多的項目。',
+        'string'  => ':attribute必須多於:value個字符。',
+        'array'   => ':attribute必須包含比:value多的項目。',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute必須大於或等於:value.',
+        'numeric' => 'The :attribute必須大於或等於:value。',
         'file'    => 'The :attribute必須大於或等於:value千字節。',
-        'string'  => 'The :attributeå¿\85é \88大æ\96¼æ\88\96ç­\89æ\96¼ï¼\9avalue字符。',
-        'array'   => 'The :attribute必須具有:value項或更多。',
+        'string'  => 'The :attributeå¿\85é \88å¤\9aæ\96¼æ\88\96ç­\89æ\96¼:valueå\80\8b字符。',
+        'array'   => ':attribute必須具有:value或更多項。',
     ],
     'exists'               => '選中的 :attribute 無效。',
     'image'                => ':attribute 必須是一個圖片。',
@@ -54,16 +54,16 @@ return [
     'ipv6'                 => 'The :attribute必須是有效的IPv6地址。',
     'json'                 => 'The :attribute必須是有效的JSON字符串。',
     'lt'                   => [
-        'numeric' => 'The :attribute必須小於:value。',
+        'numeric' => ':attribute必須小於:value。',
         'file'    => 'The :attribute必須小於:value千字節。',
-        'string'  => 'The :attribute必須小於:value字符。',
-        'array'   => 'The :attribute必須少於:value個項目。',
+        'string'  => ':attribute必須少於:value個字符。',
+        'array'   => ':attribute必須少於:value個項目。',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute必須小於或等於:value。',
-        'file'    => 'The :attribute必須小於或等於:value千字節。',
-        'string'  => 'The :attribute必須小於或等於:value字符。',
-        'array'   => 'The :attribute不得超過:value個項目。',
+        'numeric' => ':attribute必須小於或等於:value。',
+        'file'    => ':attribute必須小於或等於:value KB。',
+        'string'  => ':attribute必須少於或等於:value個字符。',
+        'array'   => ':attribute不得多過:value個項目。',
     ],
     'max'                  => [
         'numeric' => ':attribute 不能超過:max。',
@@ -74,7 +74,7 @@ return [
     'mimes'                => ':attribute 必須是 :values 類型的檔案。',
     'min'                  => [
         'numeric' => ':attribute 至少為:min。',
-        'file'    => ':attribute 至少為:min KB。',
+        'file'    => ':attribute 必須至少為:min KB。',
         'string'  => ':attribute 至少為:min個字元。',
         'array'   => ':attribute 至少有:min項。',
     ],
index b6f35376d20e57c5f7956af4ae39c51f5311b6d4..697286a78cf5f606ed06a695c4ebb73c9cd71bfd 100644 (file)
       margin-inline-end: 0px;
     }
   }
-  > div .outline input {
+  .outline input {
     margin: $-s 0;
     width: 100%;
   }
+  .outline {
+    position: relative;
+  }
   .handle {
     @include lightDark(background-color, #eee, #2d2d2d);
     left: 0;
index 683694d96b354b391247a4a3c9fdd9d296089b31..e01ecebc949c2e7154ae4186d13d3171b67bcd79 100644 (file)
   fill: currentColor !important;
 }
 
+.text-white {
+  color: #fff;
+  fill: currentColor !important;
+}
+
 /*
  * Entity text colors
  */
@@ -90,9 +95,3 @@
 .bg-shelf {
   background-color: var(--color-bookshelf);
 }
-
-.bg-shelf, .bg-book {
-  @include whenDark {
-    filter: brightness(67%) saturate(80%);
-  }
-}
index 13e16e42fe6c97ba1ad27ab3c0519b81def9d9da..eb40741d14f9932c4fbd9d20bf02b1d2380db1cf 100644 (file)
@@ -81,7 +81,7 @@
   }
 }
 
-[overlay] {
+[overlay], .popup-background {
   @include lightDark(background-color, rgba(0, 0, 0, 0.333), rgba(0, 0, 0, 0.6));
   position: fixed;
   z-index: 95536;
@@ -197,11 +197,8 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   transition: all cubic-bezier(.4, 0, 1, 1) 160ms;
   overflow: hidden;
   &.selected {
-    //transform: scale3d(0.92, 0.92, 0.92);
-    border: 4px solid #FFF;
-    overflow: hidden;
-    border-radius: 8px;
-    box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
+    transform: scale3d(0.92, 0.92, 0.92);
+    outline: currentColor 2px solid;
   }
   img {
     width: 100%;
@@ -231,7 +228,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
 }
 
-#image-manager .load-more {
+.image-manager .load-more {
   display: block;
   text-align: center;
   @include lightDark(background-color, #EEE, #444);
@@ -243,6 +240,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   font-style: italic;
 }
 
+.image-manager .loading-container {
+  text-align: center;
+}
+
 .image-manager-sidebar {
   width: 300px;
   overflow-y: auto;
@@ -250,6 +251,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   border-inline-start: 1px solid #DDD;
   @include lightDark(border-color, #ddd, #000);
   .inner {
+    min-height: auto;
     padding: $-m;
   }
   img {
@@ -291,6 +293,12 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   }
 }
 
+.image-manager .corner-button {
+  margin: 0;
+  border-radius: 0;
+  padding: $-m;
+}
+
 // Dropzone
 /*
  * The MIT License
@@ -298,7 +306,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
  */
 .dz-message {
   font-size: 1em;
-  line-height: 2.35;
+  line-height: 2.85;
   font-style: italic;
   color: #888;
   text-align: center;
@@ -601,9 +609,14 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
     display: inline-block;
     @include lightDark(color, #666, #999);
     cursor: pointer;
+    border-right: 1px solid rgba(0, 0, 0, 0.1);
+    border-bottom: 2px solid transparent;
     &.selected {
       border-bottom: 2px solid var(--color-primary);
     }
+    &:last-child {
+      border-right: 0;
+    }
   }
 }
 
@@ -611,11 +624,11 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   display: none;
 }
 
-#code-editor .CodeMirror {
+.code-editor .CodeMirror {
   height: 400px;
 }
 
-#code-editor .lang-options {
+.code-editor .lang-options {
   max-width: 480px;
   margin-bottom: $-s;
   a {
@@ -625,10 +638,10 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
 }
 
 @include smaller-than($m) {
-  #code-editor .lang-options {
+  .code-editor .lang-options {
     max-width: 100%;
   }
-  #code-editor .CodeMirror {
+  .code-editor .CodeMirror {
     height: 200px;
   }
 }
index 4c3f6c619d802db2c6fbe7f6d919a00b1280058b..e19bb4f612f373461545ff97c4b7078bc5fd31ae 100644 (file)
@@ -24,7 +24,7 @@ header {
   padding: $-xxs 0;
   @include lightDark(border-bottom-color, #DDD, #000);
   @include whenDark {
-    filter: saturate(0.6) brightness(0.8);
+    filter: saturate(0.8) brightness(0.8);
   }
   .links {
     display: inline-block;
index 595713feba11a4f08d92e05198b46b148a07da10..cf2a1630e48b1de1eab30c008176463859dc5c32 100644 (file)
@@ -49,6 +49,9 @@
   &.v-center {
     align-items: center;
   }
+  &.v-end {
+    align-items: end;
+  }
   &.no-gap {
     grid-row-gap: 0;
     grid-column-gap: 0;
@@ -118,11 +121,25 @@ body.flexbox {
   position: relative;
 }
 
+.flex-container-row {
+  display: flex;
+  flex-direction: row;
+}
+
+.flex-container-column {
+  display: flex;
+  flex-direction: column;
+}
+
 .flex {
   min-height: 0;
   flex: 1;
 }
 
+.justify-flex-end {
+  justify-content: flex-end;
+}
+
 
 /**
  * Display and float utilities
@@ -141,7 +158,7 @@ body.flexbox {
 }
 
 .hidden {
-  display: none;
+  display: none !important;
 }
 
 .float {
index 77727060e64e8bdf0d333f830deb805db148e117..a3a58e6c6cd5c66f678d2681d6744f6847748a31 100644 (file)
@@ -357,13 +357,15 @@ ul.pagination {
     border: 1px solid #CCC;
     margin-inline-start: -1px;
     user-select: none;
-    &.disabled {
-      cursor: not-allowed;
-    }
+    @include lightDark(color, #555, #eee);
+    @include lightDark(border-color, #ccc, #666);
+  }
+  li.disabled {
+    cursor: not-allowed;
   }
   li.active span {
-    @include lightDark(color, #444, #eee);
-    @include lightDark(background-color, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.5));
+    @include lightDark(color, #111, #eee);
+    @include lightDark(background-color, rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.5));
   }
 }
 
@@ -560,15 +562,15 @@ ul.pagination {
   right: 0;
   margin: $-m 0;
   @include lightDark(background-color, #fff, #333);
-  box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.1);
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.18);
   border-radius: 1px;
-  border: 1px solid #EEE;
-  @include lightDark(border-color, #eee, #000);
   min-width: 180px;
   padding: $-xs 0;
   @include lightDark(color, #555, #eee);
   fill: currentColor;
   text-align: start !important;
+  max-height: 500px;
+  overflow-y: auto;
   &.wide {
     min-width: 220px;
   }
index 1ed02d2e7bec40c957a816134b0812aa1aa50666..4f249244b03f88a388c3b6c97855b01d27ac1d0a 100755 (executable)
@@ -327,25 +327,17 @@ body.mce-fullscreen, body.markdown-fullscreen {
 }
 
 .suggestion-box {
-  position: absolute;
-  background-color: #FFF;
-  border: 1px solid #BBB;
-  box-shadow: $bs-light;
-  list-style: none;
-  z-index: 100;
+  top: auto;
+  margin: -4px 0 0;
+  right: auto;
+  left: 0;
   padding: 0;
-  margin: 0;
-  border-radius: 3px;
   li {
     display: block;
-    padding: $-xs $-s;
     border-bottom: 1px solid #DDD;
     &:last-child {
       border-bottom: 0;
     }
-    &.active {
-      background-color: #EEE;
-    }
   }
 }
 
index 57b229ab8eb3f50617c26e112f5ab2fb5e16bd4e..40217de9b344c37541c063f295c51f928c5ade0d 100644 (file)
@@ -28,5 +28,5 @@
     }
   }
 }
-@include spacing('margin', 'm')
-@include spacing('padding', 'p')
\ No newline at end of file
+@include spacing('margin', 'm');
+@include spacing('padding', 'p');
\ No newline at end of file
index 116504199f10653a93fa941733b45498128eaea7..4ada3472592e4089c30c4c1ddb3e3fec5ad39985 100644 (file)
@@ -96,9 +96,6 @@ a {
   text-decoration: none;
   transition: filter ease-in-out 80ms;
   line-height: 1.6;
-  @include whenDark {
-    filter: brightness(1.3) saturate(0.7);
-  }
   &:hover {
     text-decoration: underline;
   }
@@ -133,7 +130,7 @@ p, ul, ol, pre, table, blockquote {
 hr {
   border: 0;
   height: 1px;
-  @include lightDark(background, #eaeaea, #222);
+  @include lightDark(background, #eaeaea, #555);
   margin-bottom: $-l;
   &.faded {
     background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
index 9dbecda95bf0d5a57fe40c7b2c3cfc25401f62cb..dfaf6683ed5888a236d1db4a42aecd21618d62fa 100644 (file)
@@ -51,6 +51,9 @@
       overflow:auto;
       iframe {
         flex: 1;
+        // Force TinyMCE iframe to render on its own layer
+        // for much greater performance in Safari
+        will-change: transform;
       }
     }
   }
index 8af3634693e67d9f2f9d32bc68ae617e460518df..376541b5dcba5d67b476f269b1c60ab8aab61131 100644 (file)
 @import "lists";
 @import "pages";
 
-[v-cloak] {
-  display: none; opacity: 0;
-  animation-name: none !important;
-}
-
 // Jquery Sortable Styles
 .dragged {
   position: absolute;
@@ -140,8 +135,10 @@ $btt-size: 40px;
 
 .contained-search-box {
   display: flex;
+  height: 38px;
   input, button {
     border-radius: 0;
+    border: 1px solid #ddd;
     @include lightDark(border-color, #ddd, #000);
     margin-inline-start: -1px;
   }
@@ -162,6 +159,9 @@ $btt-size: 40px;
     background-color: $negative;
     color: #EEE;
   }
+  svg {
+    margin: 0;
+  }
 }
 
 .entity-selector {
@@ -288,4 +288,15 @@ $btt-size: 40px;
       transform: rotate(180deg);
     }
   }
+}
+
+table a.audit-log-user {
+  display: grid;
+  grid-template-columns: 42px 1fr;
+  align-items: center;
+}
+table a.icon-list-item {
+  display: grid;
+  grid-template-columns: 36px 1fr;
+  align-items: center;
 }
\ No newline at end of file
index e92b505cf3792c4b74d002b0b92f384c39ec609b..d9c3d659513507c952e3aaecac81d6f7d6c6df06 100644 (file)
                             <h6 class="text-uppercase text-muted float right">{{ $endpoint['controller_method_kebab'] }}</h6>
                             <h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m">
                                 <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
-                                {{ url($endpoint['uri']) }}
+                                @if($endpoint['controller_method_kebab'] === 'list')
+                                    <a style="color: inherit;" target="_blank" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
+                                @else
+                                    {{ url($endpoint['uri']) }}
+                                @endif
                             </h5>
                             <p class="mb-m">{{ $endpoint['description'] ?? '' }}</p>
                             @if($endpoint['body_params'] ?? false)
diff --git a/resources/views/attachments/list.blade.php b/resources/views/attachments/list.blade.php
new file mode 100644 (file)
index 0000000..8c9be82
--- /dev/null
@@ -0,0 +1,8 @@
+@foreach($attachments as $attachment)
+    <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
\ No newline at end of file
diff --git a/resources/views/attachments/manager-edit-form.blade.php b/resources/views/attachments/manager-edit-form.blade.php
new file mode 100644 (file)
index 0000000..f3f11a0
--- /dev/null
@@ -0,0 +1,47 @@
+<div component="ajax-form"
+     option:ajax-form:url="/attachments/{{ $attachment->id }}"
+     option:ajax-form:method="put"
+     option:ajax-form:success-message="{{ trans('entities.attachments_updated_success') }}">
+    <h5>{{ trans('entities.attachments_edit_file') }}</h5>
+
+    <div class="form-group">
+        <label for="attachment_edit_name">{{ trans('entities.attachments_edit_file_name') }}</label>
+        <input type="text" id="attachment_edit_name"
+               name="attachment_edit_name"
+               value="{{ $attachment_edit_name ?? $attachment->name ?? '' }}"
+               placeholder="{{ trans('entities.attachments_edit_file_name') }}">
+        @if($errors->has('attachment_edit_name'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_edit_name') }}</div>
+        @endif
+    </div>
+
+    <div component="tabs" class="tab-container">
+        <div class="nav-tabs">
+            <button refs="tabs@toggleFile" type="button" class="tab-item {{ $attachment->external ? '' : 'selected' }}">{{ trans('entities.attachments_upload') }}</button>
+            <button refs="tabs@toggleLink" type="button" class="tab-item {{ $attachment->external ? 'selected' : '' }}">{{ trans('entities.attachments_set_link') }}</button>
+        </div>
+        <div refs="tabs@contentFile" class="mb-m {{ $attachment->external ? 'hidden' : '' }}">
+            @include('components.dropzone', [
+                'placeholder' => trans('entities.attachments_edit_drop_upload'),
+                'url' =>  url('/attachments/upload/' . $attachment->id),
+                'successMessage' => trans('entities.attachments_file_updated'),
+            ])
+        </div>
+        <div refs="tabs@contentLink" class="{{ $attachment->external ? '' : 'hidden' }}">
+            <div class="form-group">
+                <label for="attachment_edit_url">{{ trans('entities.attachments_link_url') }}</label>
+                <input type="text" id="attachment_edit_url"
+                       name="attachment_edit_url"
+                       value="{{ $attachment_edit_url ?? ($attachment->external ? $attachment->path : '')  }}"
+                       placeholder="{{ trans('entities.attachment_link') }}">
+                @if($errors->has('attachment_edit_url'))
+                    <div class="text-neg text-small">{{ $errors->first('attachment_edit_url') }}</div>
+                @endif
+            </div>
+        </div>
+    </div>
+
+    <button component="event-emit-select"
+            option:event-emit-select:name="edit-back" type="button" class="button outline">{{ trans('common.back') }}</button>
+    <button refs="ajax-form@submit" type="button" class="button">{{ trans('common.save') }}</button>
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager-link-form.blade.php b/resources/views/attachments/manager-link-form.blade.php
new file mode 100644 (file)
index 0000000..6f22abb
--- /dev/null
@@ -0,0 +1,27 @@
+{{--
+@pageId
+--}}
+<div component="ajax-form"
+     option:ajax-form:url="/attachments/link"
+     option:ajax-form:method="post"
+     option:ajax-form:success-message="{{ trans('entities.attachments_link_attached') }}">
+    <input type="hidden" name="attachment_link_uploaded_to" value="{{ $pageId }}">
+    <p class="text-muted small">{{ trans('entities.attachments_explain_link') }}</p>
+    <div class="form-group">
+        <label for="attachment_link_name">{{ trans('entities.attachments_link_name') }}</label>
+        <input name="attachment_link_name" id="attachment_link_name" type="text" placeholder="{{ trans('entities.attachments_link_name') }}" value="{{ $attachment_link_name ?? '' }}">
+        @if($errors->has('attachment_link_name'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_link_name') }}</div>
+        @endif
+    </div>
+    <div class="form-group">
+        <label for="attachment_link_url">{{ trans('entities.attachments_link_url') }}</label>
+        <input name="attachment_link_url" id="attachment_link_url" type="text" placeholder="{{ trans('entities.attachments_link_url_hint') }}" value="{{ $attachment_link_url ?? '' }}">
+        @if($errors->has('attachment_link_url'))
+            <div class="text-neg text-small">{{ $errors->first('attachment_link_url') }}</div>
+        @endif
+    </div>
+    <button refs="ajax-form@submit"
+            type="button"
+            class="button">{{ trans('entities.attach') }}</button>
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager-list.blade.php b/resources/views/attachments/manager-list.blade.php
new file mode 100644 (file)
index 0000000..313faa5
--- /dev/null
@@ -0,0 +1,42 @@
+<div component="sortable-list" option:sortable-list:handle-selector=".handle">
+    @foreach($attachments as $attachment)
+        <div component="ajax-delete-row"
+             option:ajax-delete-row:url="{{ url('/attachments/' . $attachment->id) }}"
+             data-id="{{ $attachment->id }}"
+             data-drag-content="{{ json_encode(['text/html' => $attachment->htmlLink(), 'text/plain' => $attachment->markdownLink()]) }}"
+             class="card drag-card">
+            <div class="handle">@icon('grip')</div>
+            <div class="py-s">
+                <a href="{{ $attachment->getUrl() }}" target="_blank">{{ $attachment->name }}</a>
+            </div>
+            <div class="flex-fill justify-flex-end">
+                <button component="event-emit-select"
+                        option:event-emit-select:name="insert"
+                        type="button"
+                        title="{{ trans('entities.attachments_insert_link') }}"
+                        class="drag-card-action text-center text-primary">@icon('link')                 </button>
+                <button component="event-emit-select"
+                        option:event-emit-select:name="edit"
+                        option:event-emit-select:id="{{ $attachment->id }}"
+                        type="button"
+                        title="{{ trans('common.edit') }}"
+                        class="drag-card-action text-center text-primary">@icon('edit')</button>
+                <div component="dropdown" class="flex-fill relative">
+                    <button refs="dropdown@toggle"
+                            type="button"
+                            title="{{ trans('common.delete') }}"
+                            class="drag-card-action text-center text-neg">@icon('close')</button>
+                    <div refs="dropdown@menu" class="dropdown-menu">
+                        <p class="text-neg small px-m mb-xs">{{ trans('entities.attachments_delete') }}</p>
+                        <button refs="ajax-delete-row@delete" type="button" class="text-primary small delete">{{ trans('common.confirm') }}</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    @endforeach
+    @if (count($attachments) === 0)
+        <p class="small text-muted">
+            {{ trans('entities.attachments_no_files') }}
+        </p>
+    @endif
+</div>
\ No newline at end of file
diff --git a/resources/views/attachments/manager.blade.php b/resources/views/attachments/manager.blade.php
new file mode 100644 (file)
index 0000000..4bfa976
--- /dev/null
@@ -0,0 +1,39 @@
+<div style="display: block;" toolbox-tab-content="files"
+     component="attachments"
+     option:attachments:page-id="{{ $page->id ?? 0 }}">
+
+    <h4>{{ trans('entities.attachments') }}</h4>
+    <div class="px-l files">
+
+        <div refs="attachments@listContainer">
+            <p class="text-muted small">{{ trans('entities.attachments_explain') }} <span class="text-warn">{{ trans('entities.attachments_explain_instant_save') }}</span></p>
+
+            <div component="tabs" refs="attachments@mainTabs" class="tab-container">
+                <div class="nav-tabs">
+                    <button refs="tabs@toggleItems" type="button" class="selected tab-item">{{ trans('entities.attachments_items') }}</button>
+                    <button refs="tabs@toggleUpload" type="button" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
+                    <button refs="tabs@toggleLinks" type="button" class="tab-item">{{ trans('entities.attachments_link') }}</button>
+                </div>
+                <div refs="tabs@contentItems attachments@list">
+                    @include('attachments.manager-list', ['attachments' => $page->attachments->all()])
+                </div>
+                <div refs="tabs@contentUpload" class="hidden">
+                    @include('components.dropzone', [
+                        'placeholder' => trans('entities.attachments_dropzone'),
+                        'url' =>  url('/attachments/upload?uploaded_to=' . $page->id),
+                        'successMessage' => trans('entities.attachments_file_uploaded'),
+                    ])
+                </div>
+                <div refs="tabs@contentLinks" class="hidden">
+                    @include('attachments.manager-link-form', ['pageId' => $page->id])
+                </div>
+            </div>
+
+        </div>
+
+        <div refs="attachments@editContainer" class="hidden">
+
+        </div>
+
+    </div>
+</div>
\ No newline at end of file
index a3235036e02c81c76bfefa799787d3cfd27bf7e3..840d0604c8e0c22185d6e33ea43eac02e880ff88 100644 (file)
@@ -31,7 +31,7 @@
         <label for="tag-manager">{{ trans('entities.book_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => $book ?? null, 'entityType' => 'chapter'])
+        @include('components.tag-manager', ['entity' => $book ?? null])
     </div>
 </div>
 
index e3a536fc9cd7d8ab5931927198af77e31791af54..def198bddac2450b34958534cd4572da01ee0ba5 100644 (file)
@@ -1,9 +1,9 @@
 @extends('tri-layout')
 
 @section('container-attrs')
-    id="entity-dashboard"
-    entity-id="{{ $book->id }}"
-    entity-type="book"
+    component="entity-search"
+    option:entity-search:entity-id="{{ $book->id }}"
+    option:entity-search:entity-type="book"
 @stop
 
 @section('body')
     </div>
 
     <main 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>
+        <h1 class="break-text">{{$book->name}}</h1>
+        <div refs="entity-search@contentView" class="book-content">
+            <p class="text-muted">{!! nl2br(e($book->description)) !!}</p>
             @if(count($bookChildren) > 0)
-                <div class="entity-list book-contents" v-pre>
+                <div class="entity-list book-contents">
                     @foreach($bookChildren as $childElement)
                         @if($childElement->isA('chapter'))
                             @include('chapters.list-item', ['chapter' => $childElement])
@@ -29,7 +29,7 @@
                     @endforeach
                 </div>
             @else
-                <div class="mt-xl" v-pre>
+                <div class="mt-xl">
                     <hr>
                     <p class="text-muted italic mb-m mt-xl">{{ trans('entities.books_empty_contents') }}</p>
 
@@ -52,7 +52,7 @@
             @endif
         </div>
 
-        @include('partials.entity-dashboard-search-results')
+        @include('partials.entity-search-results')
     </main>
 
 @stop
 
 @section('left')
 
-    @include('partials.entity-dashboard-search-box')
+    @include('partials.entity-search-form', ['label' => trans('entities.books_search_this')])
 
     @if($book->tags->count() > 0)
         <div class="mb-xl">
index cd240e685dd651f5501a4659e74d8e145c0c1760..60cfe6674f1b7eeaf7382575a24e970264698593 100644 (file)
@@ -16,7 +16,7 @@
         <label for="tags">{{ trans('entities.chapter_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => isset($chapter)?$chapter:null, 'entityType' => 'chapter'])
+        @include('components.tag-manager', ['entity' => $chapter ?? null])
     </div>
 </div>
 
index 105cda760ff496c909aadf723c274fd12b64637b..db02ebcc4f9ae6b6d686a591a8eb2398f5d905d8 100644 (file)
@@ -1,9 +1,9 @@
 @extends('tri-layout')
 
 @section('container-attrs')
-    id="entity-dashboard"
-    entity-id="{{ $chapter->id }}"
-    entity-type="chapter"
+    component="entity-search"
+    option:entity-search:entity-id="{{ $chapter->id }}"
+    option:entity-search:entity-type="chapter"
 @stop
 
 @section('body')
     </div>
 
     <main 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>
+        <h1 class="break-text">{{ $chapter->name }}</h1>
+        <div refs="entity-search@contentView" class="chapter-content">
+            <p class="text-muted break-text">{!! nl2br(e($chapter->description)) !!}</p>
             @if(count($pages) > 0)
-                <div v-pre class="entity-list book-contents">
+                <div class="entity-list book-contents">
                     @foreach($pages as $page)
                         @include('pages.list-item', ['page' => $page])
                     @endforeach
                 </div>
             @else
-                <div class="mt-xl" v-pre>
+                <div class="mt-xl">
                     <hr>
                     <p class="text-muted italic mb-m mt-xl">{{ trans('entities.chapters_empty') }}</p>
 
@@ -49,7 +49,7 @@
             @endif
         </div>
 
-        @include('partials.entity-dashboard-search-results')
+        @include('partials.entity-search-results')
     </main>
 
 @stop
 
 @section('left')
 
-    @include('partials.entity-dashboard-search-box')
+    @include('partials.entity-search-form', ['label' => trans('entities.chapters_search_this')])
 
     @if($chapter->tags->count() > 0)
         <div class="mb-xl">
index ea96a9250dd24a22f2bf274fc093f52edcd6454e..322477ebdc4e98783c8a666043b048ae36fda804 100644 (file)
@@ -27,9 +27,9 @@
                     <button type="button" class="text-button" action="reply" aria-label="{{ trans('common.reply') }}" title="{{ trans('common.reply') }}">@icon('reply')</button>
                 @endif
                 @if(userCan('comment-delete', $comment))
-                    <div dropdown class="dropdown-container">
-                        <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
-                        <ul class="dropdown-menu" role="menu">
+                    <div component="dropdown" class="dropdown-container">
+                        <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" class="text-button" title="{{ trans('common.delete') }}">@icon('delete')</button>
+                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                             <li class="px-m text-small text-muted pb-s">{{trans('entities.comment_delete_confirm')}}</li>
                             <li><button action="delete" type="button" class="text-button text-neg" >@icon('delete'){{ trans('common.delete') }}</button></li>
                         </ul>
index fc81f13ee73053e4c4e208f64cc64e9eb8183826..140d0d027d7ad1996a6795bec9cb61a9621769d9 100644 (file)
@@ -1,23 +1,23 @@
-<section page-comments page-id="{{ $page->id }}" class="comments-list" aria-label="{{ trans('entities.comments') }}">
+<section component="page-comments"
+         option:page-comments:page-id="{{ $page->id }}"
+         option:page-comments:updated-text="{{ trans('entities.comment_updated_success') }}"
+         option:page-comments:deleted-text="{{ trans('entities.comment_deleted_success') }}"
+         option:page-comments:created-text="{{ trans('entities.comment_created_success') }}"
+         option:page-comments:count-text="{{ trans('entities.comment_count') }}"
+         class="comments-list"
+         aria-label="{{ trans('entities.comments') }}">
 
-    @exposeTranslations([
-        'entities.comment_updated_success',
-        'entities.comment_deleted_success',
-        'entities.comment_created_success',
-        'entities.comment_count',
-    ])
-
-    <div comment-count-bar class="grid half left-focus v-center no-row-gap">
+    <div refs="page-comments@commentCountBar" class="grid half left-focus v-center no-row-gap">
         <h5 comments-title>{{ trans_choice('entities.comment_count', count($page->comments), ['count' => count($page->comments)]) }}</h5>
         @if (count($page->comments) === 0 && userCan('comment-create-all'))
-            <div class="text-m-right" comment-add-button-container>
+            <div class="text-m-right" refs="page-comments@addButtonContainer">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
             </div>
         @endif
     </div>
 
-    <div class="comment-container" comment-container>
+    <div refs="page-comments@commentContainer" class="comment-container">
         @foreach($page->comments as $comment)
             @include('comments.comment', ['comment' => $comment])
         @endforeach
@@ -27,7 +27,7 @@
         @include('comments.create')
 
         @if (count($page->comments) > 0)
-            <div class="text-right" comment-add-button-container>
+            <div refs="page-comments@addButtonContainer" class="text-right">
                 <button type="button" action="addComment"
                         class="button outline">{{ trans('entities.comment_add') }}</button>
             </div>
index 61e41a354fab3214883296238e9ee55ac7c9a130..12216b95b9bdb1f5ef33bfc3403094773b5aa73b 100644 (file)
@@ -1,6 +1,7 @@
-<div class="comment-box" comment-box style="display:none;">
+<div class="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 refs="page-comments@replyToRow" 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>']) !!}
@@ -10,7 +11,8 @@
             </div>
         </div>
     </div>
-    <div class="content px-s" comment-form-container>
+
+    <div refs="page-comments@formContainer" class="content px-s">
         <form novalidate>
             <div class="form-group description-input">
                         <textarea name="markdown" rows="3"
@@ -26,4 +28,5 @@
             </div>
         </form>
     </div>
+
 </div>
\ No newline at end of file
index 4937f7cb0db7944baf74eb2389cfaac1ef3ac932..827abcac601d8d6e4276880085a1a27aadb7a960 100644 (file)
                 </div>
                 @if(signedInUser())
                     <?php $currentUser = user(); ?>
-                    <div class="dropdown-container" dropdown>
-                        <span class="user-name py-s hide-under-l" dropdown-toggle
+                    <div class="dropdown-container" component="dropdown">
+                        <span class="user-name py-s hide-under-l" refs="dropdown@toggle"
                               aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.profile_menu') }}" tabindex="0">
                             <img class="avatar" src="{{$currentUser->getAvatar(30)}}" alt="{{ $currentUser->name }}">
                             <span class="name">{{ $currentUser->getShortName(9) }}</span> @icon('caret-down')
                         </span>
-                        <ul class="dropdown-menu" role="menu">
+                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                             <li>
                                 <a href="{{ url("/user/{$currentUser->id}") }}">@icon('user'){{ trans('common.view_profile') }}</a>
                             </li>
index 63b76aa108b4c73d82bbc8899305df924801540a..2631f1a57ed878b01ad5099f7a539e07169eba58 100644 (file)
@@ -66,6 +66,4 @@
         </div>
     </div>
 
-
-
 @stop
index 15f6ae252fdd09c1331c78e972b76888512018d4..6822bb28d00ad1823571c0bcfceaa044d0682a6b 100644 (file)
@@ -1,10 +1,10 @@
-<div id="code-editor">
-    <div overlay ref="overlay" v-cloak @click="hide()">
-        <div class="popup-body" tabindex="-1" @click.stop @keydown.enter.ctrl="save">
+<div>
+    <div components="popup code-editor" class="popup-background code-editor">
+        <div refs="code-editor@container" class="popup-body" tabindex="-1">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.code_editor') }}</div>
-                <button class="popup-header-close" @click="hide()">x</button>
+                <button class="popup-header-close" refs="popup@hide">x</button>
             </div>
 
             <div class="p-l popup-content">
                     <label for="code-editor-language">{{ trans('components.code_language') }}</label>
                     <div class="lang-options">
                         <small>
-                            <a @click="updateLanguage('CSS')">CSS</a>
-                            <a @click="updateLanguage('C')">C</a>
-                            <a @click="updateLanguage('C++')">C++</a>
-                            <a @click="updateLanguage('C#')">C#</a>
-                            <a @click="updateLanguage('Fortran')">Fortran</a>
-                            <a @click="updateLanguage('Go')">Go</a>
-                            <a @click="updateLanguage('HTML')">HTML</a>
-                            <a @click="updateLanguage('INI')">INI</a>
-                            <a @click="updateLanguage('Java')">Java</a>
-                            <a @click="updateLanguage('JavaScript')">JavaScript</a>
-                            <a @click="updateLanguage('JSON')">JSON</a>
-                            <a @click="updateLanguage('Lua')">Lua</a>
-                            <a @click="updateLanguage('MarkDown')">MarkDown</a>
-                            <a @click="updateLanguage('Nginx')">Nginx</a>
-                            <a @click="updateLanguage('PASCAL')">Pascal</a>
-                            <a @click="updateLanguage('Perl')">Perl</a>
-                            <a @click="updateLanguage('PHP')">PHP</a>
-                            <a @click="updateLanguage('Powershell')">Powershell</a>
-                            <a @click="updateLanguage('Python')">Python</a>
-                            <a @click="updateLanguage('Ruby')">Ruby</a>
-                            <a @click="updateLanguage('shell')">Shell/Bash</a>
-                            <a @click="updateLanguage('SQL')">SQL</a>
-                            <a @click="updateLanguage('XML')">XML</a>
-                            <a @click="updateLanguage('YAML')">YAML</a>
+                            <a refs="code-editor@languageLink" data-lang="CSS">CSS</a>
+                            <a refs="code-editor@languageLink" data-lang="C">C</a>
+                            <a refs="code-editor@languageLink" data-lang="C++">C++</a>
+                            <a refs="code-editor@languageLink" data-lang="C#">C#</a>
+                            <a refs="code-editor@languageLink" data-lang="Fortran">Fortran</a>
+                            <a refs="code-editor@languageLink" data-lang="Go">Go</a>
+                            <a refs="code-editor@languageLink" data-lang="HTML">HTML</a>
+                            <a refs="code-editor@languageLink" data-lang="INI">INI</a>
+                            <a refs="code-editor@languageLink" data-lang="Java">Java</a>
+                            <a refs="code-editor@languageLink" data-lang="JavaScript">JavaScript</a>
+                            <a refs="code-editor@languageLink" data-lang="JSON">JSON</a>
+                            <a refs="code-editor@languageLink" data-lang="Lua">Lua</a>
+                            <a refs="code-editor@languageLink" data-lang="MarkDown">MarkDown</a>
+                            <a refs="code-editor@languageLink" data-lang="Nginx">Nginx</a>
+                            <a refs="code-editor@languageLink" data-lang="PASCAL">Pascal</a>
+                            <a refs="code-editor@languageLink" data-lang="Perl">Perl</a>
+                            <a refs="code-editor@languageLink" data-lang="PHP">PHP</a>
+                            <a refs="code-editor@languageLink" data-lang="Powershell">Powershell</a>
+                            <a refs="code-editor@languageLink" data-lang="Python">Python</a>
+                            <a refs="code-editor@languageLink" data-lang="Ruby">Ruby</a>
+                            <a refs="code-editor@languageLink" data-lang="shell">Shell/Bash</a>
+                            <a refs="code-editor@languageLink" data-lang="SQL">SQL</a>
+                            <a refs="code-editor@languageLink" data-lang="XML">XML</a>
+                            <a refs="code-editor@languageLink" data-lang="YAML">YAML</a>
                         </small>
                     </div>
-                    <input @keypress.enter="save()" id="code-editor-language" type="text" @input="updateEditorMode(language)" v-model="language">
+                    <input refs="code-editor@languageInput" id="code-editor-language" type="text">
                 </div>
 
                 <div class="form-group">
-                    <label for="code-editor-content">{{ trans('components.code_content') }}</label>
-                    <textarea ref="editor" v-model="code"></textarea>
+                    <div class="grid half no-break v-end mb-xs">
+                        <div>
+                            <label for="code-editor-content">{{ trans('components.code_content') }}</label>
+                        </div>
+                        <div class="text-right">
+                            <div component="dropdown" refs="code-editor@historyDropDown" class="inline block">
+                                <button refs="dropdown@toggle" class="text-button text-small">@icon('history') {{ trans('components.code_session_history') }}</button>
+                                <ul refs="dropdown@menu code-editor@historyList" class="dropdown-menu"></ul>
+                            </div>
+                        </div>
+                    </div>
+
+                    <div class="clearfix"></div>
+                    <textarea refs="code-editor@editor"></textarea>
                 </div>
 
                 <div class="form-group">
-                    <button type="button" class="button" @click="save()">{{ trans('components.code_save') }}</button>
+                    <button refs="code-editor@saveButton" type="button" class="button">{{ trans('components.code_save') }}</button>
                 </div>
 
             </div>
 
         </div>
     </div>
-</div>
+</div>
\ No newline at end of file
diff --git a/resources/views/components/dropzone.blade.php b/resources/views/components/dropzone.blade.php
new file mode 100644 (file)
index 0000000..6c5ac49
--- /dev/null
@@ -0,0 +1,15 @@
+{{--
+@url - URL to upload to.
+@placeholder - Placeholder text
+@successMessage
+--}}
+<div component="dropzone"
+     option:dropzone:url="{{ $url }}"
+     option:dropzone:success-message="{{ $successMessage ?? '' }}"
+     option:dropzone:remove-message="{{ trans('components.image_upload_remove') }}"
+     option:dropzone:upload-limit-message="{{ trans('errors.server_upload_limit') }}"
+     option:dropzone:timeout-message="{{ trans('errors.file_upload_timeout') }}"
+
+     class="dropzone-container text-center">
+    <button type="button" class="dz-message">{{ $placeholder }}</button>
+</div>
\ No newline at end of file
index 0beee658d88334bd598d79b185dae116fe6aa838..ec8712b6a5bae9d533009d2f0c6b49b22e2c1a65 100644 (file)
@@ -1,13 +1,13 @@
 <div id="entity-selector-wrap">
-    <div overlay entity-selector-popup>
+    <div components="popup entity-selector-popup" class="popup-background">
         <div class="popup-body small" tabindex="-1">
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('entities.entity_select') }}</div>
-                <button type="button" class="popup-header-close">x</button>
+                <button refs="popup@hide" 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 corner-button">{{ trans('common.select') }}</button>
+                <button refs="entity-selector-popup@select" type="button" disabled="true" class="button corner-button">{{ trans('common.select') }}</button>
             </div>
         </div>
     </div>
diff --git a/resources/views/components/image-manager-form.blade.php b/resources/views/components/image-manager-form.blade.php
new file mode 100644 (file)
index 0000000..e49a5fc
--- /dev/null
@@ -0,0 +1,60 @@
+<div class="image-manager-details">
+
+    <form component="ajax-form"
+          option:ajax-form:success-message="{{ trans('components.image_update_success') }}"
+          option:ajax-form:method="put"
+          option:ajax-form:response-container=".image-manager-details"
+          option:ajax-form:url="{{ url('images/' . $image->id) }}">
+
+        <div class="image-manager-viewer">
+            <a href="{{ $image->url }}" target="_blank" class="block">
+                <img src="{{ $image->thumbs['display'] }}"
+                     alt="{{ $image->name }}"
+                     class="anim fadeIn"
+                     title="{{ $image->name }}">
+            </a>
+        </div>
+        <div class="form-group stretch-inputs">
+            <label for="name">{{ trans('components.image_image_name') }}</label>
+            <input id="name" class="input-base" type="text" name="name" value="{{ $image->name }}">
+        </div>
+        <div class="grid half">
+            <div>
+                <button type="button"
+                        id="image-manager-delete"
+                        title="{{ trans('common.delete') }}"
+                        class="button icon outline">@icon('delete')</button>
+            </div>
+            <div class="text-right">
+                <button type="submit"
+                        class="button icon outline">{{ trans('common.save') }}</button>
+            </div>
+        </div>
+    </form>
+
+    @if(!is_null($dependantPages))
+        @if(count($dependantPages) > 0)
+            <p class="text-neg mb-xs mt-m">{{ trans('components.image_delete_used') }}</p>
+            <ul class="text-neg">
+                @foreach($dependantPages as $page)
+                    <li>
+                        <a href="{{ $page->url }}"
+                           target="_blank"
+                           class="text-neg">{{ $page->name }}</a>
+                    </li>
+                @endforeach
+            </ul>
+        @endif
+        <p class="text-neg mb-xs">{{ trans('components.image_delete_confirm_text') }}</p>
+        <form component="ajax-form"
+              option:ajax-form:success-message="{{ trans('components.image_delete_success') }}"
+              option:ajax-form:method="delete"
+              option:ajax-form:response-container=".image-manager-details"
+              option:ajax-form:url="{{ url('images/' . $image->id) }}">
+            <button type="submit" class="button neg">
+                {{ trans('common.delete_confirm') }}
+            </button>
+        </form>
+    @endif
+
+</div>
\ No newline at end of file
diff --git a/resources/views/components/image-manager-list.blade.php b/resources/views/components/image-manager-list.blade.php
new file mode 100644 (file)
index 0000000..e5562e1
--- /dev/null
@@ -0,0 +1,23 @@
+@foreach($images as $index => $image)
+<div>
+    <div component="event-emit-select"
+         option:event-emit-select:name="image"
+         option:event-emit-select:data="{{ json_encode($image) }}"
+         class="image anim fadeIn text-primary"
+         style="animation-delay: {{ $index > 26 ? '160ms' : ($index * 25) . 'ms' }};">
+        <img src="{{ $image->thumbs['gallery'] }}"
+             alt="{{ $image->name }}"
+             width="150"
+             height="150"
+             loading="lazy"
+             title="{{ $image->name }}">
+        <div class="image-meta">
+            <span class="name">{{ $image->name }}</span>
+            <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => $image->created_at->format('Y-m-d H:i:s')]) }}</span>
+        </div>
+    </div>
+</div>
+@endforeach
+@if($hasMore)
+    <div class="load-more">{{ trans('components.image_load_more') }}</div>
+@endif
\ No newline at end of file
index 3eed2fdb7a50b3361a81d0af9135faea55625af8..4f03eeaec21c33634992c040bbb56014a6ada5ff 100644 (file)
-<div id="image-manager" image-type="{{ $imageType }}" uploaded-to="{{ $uploaded_to ?? 0 }}">
+<div component="image-manager"
+     option:image-manager:uploaded-to="{{ $uploaded_to ?? 0 }}"
+     class="image-manager">
 
-    @exposeTranslations([
-        'components.image_delete_success',
-        'components.image_upload_success',
-        'errors.server_upload_limit',
-        'components.image_upload_remove',
-        'components.file_upload_timeout',
-    ])
-
-    <div overlay v-cloak @click="hide">
-        <div class="popup-body" tabindex="-1" @click.stop>
+    <div component="popup"
+         refs="image-manager@popup"
+         class="popup-background">
+        <div class="popup-body" tabindex="-1">
 
             <div class="popup-header primary-background">
                 <div class="popup-title">{{ trans('components.image_select') }}</div>
-                <button class="popup-header-close" @click="hide()">x</button>
+                <button refs="popup@hide" type="button" class="popup-header-close">x</button>
             </div>
 
             <div class="flex-fill image-manager-body">
 
                 <div class="image-manager-content">
-                    <div v-if="imageType === 'gallery' || imageType === 'drawio'" class="image-manager-header primary-background-light nav-tabs grid third">
-                        <div class="tab-item" title="{{ trans('components.image_all_title') }}" :class="{selected: !filter}" @click="setFilterType(null)">@icon('images') {{ trans('components.image_all') }}</div>
-                        <div class="tab-item" title="{{ trans('components.image_book_title') }}" :class="{selected: (filter=='book')}" @click="setFilterType('book')">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</div>
-                        <div class="tab-item" title="{{ trans('components.image_page_title') }}" :class="{selected: (filter=='page')}" @click="setFilterType('page')">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</div>
+                    <div class="image-manager-header primary-background-light nav-tabs grid third no-gap">
+                        <button refs="image-manager@filterTabs"
+                                data-filter="all"
+                                type="button" class="tab-item selected" title="{{ trans('components.image_all_title') }}">@icon('images') {{ trans('components.image_all') }}</button>
+                        <button refs="image-manager@filterTabs"
+                                data-filter="book"
+                                type="button" class="tab-item" title="{{ trans('components.image_book_title') }}">@icon('book', ['class' => 'text-book svg-icon']) {{ trans('entities.book') }}</button>
+                        <button refs="image-manager@filterTabs"
+                                data-filter="page"
+                                type="button" class="tab-item" title="{{ trans('components.image_page_title') }}">@icon('page', ['class' => 'text-page svg-icon']) {{ trans('entities.page') }}</button>
                     </div>
                     <div>
-                        <form @submit.prevent="searchImages" class="contained-search-box">
-                            <input placeholder="{{ trans('components.image_search_hint') }}" v-model="searchTerm" type="text">
-                            <button :class="{active: searching}" title="{{ trans('common.search_clear') }}" type="button" @click="cancelSearch()" class="text-button cancel">@icon('close')</button>
-                            <button title="{{ trans('common.search') }}" class="text-button">@icon('search')</button>
+                        <form refs="image-manager@searchForm" class="contained-search-box">
+                            <input refs="image-manager@searchInput"
+                                   placeholder="{{ trans('components.image_search_hint') }}"
+                                   type="text">
+                            <button refs="image-manager@cancelSearch"
+                                    title="{{ trans('common.search_clear') }}"
+                                    type="button"
+                                    class="cancel">@icon('close')</button>
+                            <button type="submit" class="primary-background text-white"
+                                    title="{{ trans('common.search') }}">@icon('search')</button>
                         </form>
                     </div>
-                    <div class="image-manager-list">
-                        <div v-if="images.length > 0" v-for="(image, idx) in images">
-                            <div class="image anim fadeIn" :style="{animationDelay: (idx > 26) ? '160ms' : ((idx * 25) + 'ms')}"
-                                 :class="{selected: (image==selectedImage)}" @click="imageSelect(image)">
-                                <img :src="image.thumbs.gallery" :alt="image.title" :title="image.name">
-                                <div class="image-meta">
-                                    <span class="name" v-text="image.name"></span>
-                                    <span class="date">{{ trans('components.image_uploaded', ['uploadedDate' => "{{ getDate(image.created_at) }" . "}"]) }}</span>
-                                </div>
-                            </div>
-                        </div>
-                        <div class="load-more" v-show="hasMore" @click="fetchData">{{ trans('components.image_load_more') }}</div>
-                    </div>
+                    <div refs="image-manager@listContainer" class="image-manager-list"></div>
                 </div>
 
-                <div class="image-manager-sidebar">
-
-                    <dropzone v-if="imageType !== 'drawio'" ref="dropzone" placeholder="{{ trans('components.image_dropzone') }}" :upload-url="uploadUrl" :uploaded-to="uploadedTo" @success="uploadSuccess"></dropzone>
-
-                    <div class="inner">
-
-                        <div class="image-manager-details anim fadeIn" v-if="selectedImage">
-
-                            <form @submit.prevent="saveImageDetails">
-                                <div class="image-manager-viewer">
-                                    <a :href="selectedImage.url" target="_blank" style="display: block;">
-                                        <img :src="selectedImage.thumbs.display" :alt="selectedImage.name"
-                                             :title="selectedImage.name">
-                                    </a>
-                                </div>
-                                <div class="form-group">
-                                    <label for="name">{{ trans('components.image_image_name') }}</label>
-                                    <input id="name" class="input-base" name="name" v-model="selectedImage.name">
-                                </div>
-                            </form>
-
-                            <div class="clearfix">
-                                <div class="float left">
-                                    <button type="button" class="button icon outline" @click="deleteImage">@icon('delete')</button>
-
-                                </div>
-                                <button class="button anim fadeIn float right" v-show="selectedImage" @click="callbackAndHide(selectedImage)">
-                                    {{ trans('components.image_select_image') }}
-                                </button>
-                                <div class="clearfix"></div>
-                                <div v-show="dependantPages">
-                                    <p class="text-neg text-small">
-                                        {{ trans('components.image_delete_used') }}
-                                    </p>
-                                    <ul class="text-neg">
-                                        <li v-for="page in dependantPages">
-                                            <a :href="page.url" target="_blank" class="text-neg" v-text="page.name"></a>
-                                        </li>
-                                    </ul>
-                                </div>
-                                <div v-show="deleteConfirm" class="text-neg text-small">
-                                    {{ trans('components.image_delete_confirm') }}
-                                </div>
-                            </div>
-
-                        </div>
+                <div class="image-manager-sidebar flex-container-column">
 
+                    <div refs="image-manager@dropzoneContainer">
+                        @include('components.dropzone', [
+                            'placeholder' => trans('components.image_dropzone'),
+                            'successMessage' => trans('components.image_upload_success'),
+                            'url' => url('/images/gallery?' . http_build_query(['uploaded_to' => $uploaded_to ?? 0]))
+                        ])
+                    </div>
 
+                    <div refs="image-manager@formContainer" class="inner flex"></div>
 
-                    </div>
+                    <button refs="image-manager@selectButton" type="button" class="hidden button corner-button">
+                        {{ trans('components.image_select_image') }}
+                    </button>
                 </div>
 
             </div>
diff --git a/resources/views/components/tag-manager-list.blade.php b/resources/views/components/tag-manager-list.blade.php
new file mode 100644 (file)
index 0000000..6fbce2f
--- /dev/null
@@ -0,0 +1,25 @@
+@foreach(array_merge($tags, [null, null]) as $index => $tag)
+    <div class="card drag-card {{ $loop->last ? 'hidden' : '' }}" @if($loop->last) refs="add-remove-rows@model" @endif>
+        <div class="handle">@icon('grip')</div>
+        @foreach(['name', 'value'] as $type)
+            <div component="auto-suggest"
+                 option:auto-suggest:url="{{ url('/ajax/tags/suggest/' . $type . 's') }}"
+                 option:auto-suggest:type="{{ $type }}"
+                 class="outline">
+                <input value="{{ $tag->$type ?? '' }}"
+                       placeholder="{{ trans('entities.tag_' . $type) }}"
+                       aria-label="{{ trans('entities.tag_' . $type) }}"
+                       name="tags[{{ $loop->parent->last ? 'randrowid' : $index }}][{{ $type }}]"
+                       type="text"
+                       refs="auto-suggest@input"
+                       autocomplete="off"/>
+                <ul refs="auto-suggest@list" class="suggestion-box dropdown-menu"></ul>
+            </div>
+        @endforeach
+        <button type="button"
+                aria-label="{{ trans('entities.tags_remove') }}"
+                class="text-center drag-card-action text-neg">
+            @icon('close')
+        </button>
+    </div>
+@endforeach
\ No newline at end of file
index 2878569374d6db6bd80928a3fe34d8477d982124..9e24ba3fdcedadae6140c81122e75fb4fd33750c 100644 (file)
@@ -1,22 +1,16 @@
-<div id="tag-manager" entity-id="{{ isset($entity) ? $entity->id : 0 }}" entity-type="{{ $entity ? $entity->getType() : $entityType }}">
-    <div class="tags">
+<div components="tag-manager add-remove-rows"
+     option:add-remove-rows:row-selector=".card"
+     option:add-remove-rows:remove-selector="button.text-neg"
+     option:tag-manager:row-selector=".card:not(.hidden)"
+     refs="tag-manager@add-remove"
+     class="tags">
+
         <p class="text-muted small">{!! nl2br(e(trans('entities.tags_explain'))) !!}</p>
 
-        <draggable :options="{handle: '.handle'}" :list="tags" element="div">
-            <div v-for="(tag, i) in tags" :key="tag.key" class="card drag-card">
-                <div class="handle" >@icon('grip')</div>
-                <div>
-                    <autosuggest url="{{ url('/ajax/tags/suggest/names') }}" type="name" class="outline" :name="getTagFieldName(i, 'name')"
-                                 v-model="tag.name" @input="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_name') }}"/>
-                </div>
-                <div>
-                    <autosuggest url="{{ url('/ajax/tags/suggest/values') }}" type="value" class="outline" :name="getTagFieldName(i, 'value')"
-                                 v-model="tag.value" @change="tagChange(tag)" @blur="tagBlur(tag)" placeholder="{{ trans('entities.tag_value') }}"/>
-                </div>
-                <button type="button" aria-label="{{ trans('entities.tags_remove') }}" v-show="tags.length !== 1" class="text-center drag-card-action text-neg" @click="removeTag(tag)">@icon('close')</button>
-            </div>
-        </draggable>
+        <div component="sortable-list"
+             option:sortable-list:handle-selector=".handle">
+            @include('components.tag-manager-list', ['tags' => $entity ? $entity->tags->all() : []])
+        </div>
 
-        <button @click="addEmptyTag" type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
-    </div>
+        <button refs="add-remove-rows@add" type="button" class="text-button">{{ trans('entities.tags_add') }}</button>
 </div>
\ No newline at end of file
index 580b332f39c7e1fc993c670693ba08a8ad818192..fc6ad93a8d13284680fd7fc5b267c2ce6453d89d 100644 (file)
@@ -3,10 +3,10 @@
     @foreach($roles as $role)
         <div>
             @include('components.custom-checkbox', [
-                'name' => $name . '[' . str_replace('.', 'DOT', $role->name) . ']',
+                'name' => $name . '[' . strval($role->id) . ']',
                 'label' => $role->display_name,
                 'value' => $role->id,
-                'checked' => old($name . '.' . str_replace('.', 'DOT', $role->name)) || (!old('name') && isset($model) && $model->hasRole($role->name))
+                'checked' => old($name . '.' . strval($role->id)) || (!old('name') && isset($model) && $model->hasRole($role->id))
             ])
         </div>
     @endforeach
diff --git a/resources/views/pages/attachment-manager.blade.php b/resources/views/pages/attachment-manager.blade.php
deleted file mode 100644 (file)
index dd00678..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-<div toolbox-tab-content="files" id="attachment-manager" page-id="{{ $page->id ?? 0 }}">
-
-    @exposeTranslations([
-    'entities.attachments_file_uploaded',
-    'entities.attachments_file_updated',
-    'entities.attachments_link_attached',
-    'entities.attachments_updated_success',
-    'errors.server_upload_limit',
-    'components.image_upload_remove',
-    'components.file_upload_timeout',
-    ])
-
-    <h4>{{ trans('entities.attachments') }}</h4>
-    <div class="px-l files">
-
-        <div id="file-list" v-show="!fileToEdit">
-            <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">
-                    <button type="button" @click="tab = 'list'" :class="{selected: tab === 'list'}"
-                            class="tab-item">{{ trans('entities.attachments_items') }}</button>
-                    <button type="button" @click="tab = 'file'" :class="{selected: tab === 'file'}"
-                            class="tab-item">{{ trans('entities.attachments_upload') }}</button>
-                    <button type="button" @click="tab = 'link'" :class="{selected: tab === 'link'}"
-                            class="tab-item">{{ trans('entities.attachments_link') }}</button>
-                </div>
-                <div v-show="tab === 'list'">
-                    <draggable style="width: 100%;" :options="{handle: '.handle'}" @change="fileSortUpdate" :list="files" element="div">
-                        <div v-for="(file, index) in files" :key="file.id" class="card drag-card">
-                            <div class="handle">@icon('grip')</div>
-                            <div class="py-s">
-                                <a :href="getFileUrl(file)" target="_blank" v-text="file.name"></a>
-                                <div v-if="file.deleting">
-                                    <span class="text-neg small">{{ trans('entities.attachments_delete_confirm') }}</span>
-                                    <br>
-                                    <button type="button" class="text-primary small" @click="file.deleting = false;">{{ trans('common.cancel') }}</button>
-                                </div>
-                            </div>
-                            <button type="button" @click="startEdit(file)" class="drag-card-action text-center text-primary">@icon('edit')</button>
-                            <button type="button" @click="deleteFile(file)" class="drag-card-action text-center text-neg">@icon('close')</button>
-                        </div>
-                    </draggable>
-                    <p class="small text-muted" v-if="files.length === 0">
-                        {{ trans('entities.attachments_no_files') }}
-                    </p>
-                </div>
-                <div v-show="tab === 'file'">
-                    <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="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 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 text-neg" v-for="error in errors.link.link" v-text="error"></p>
-                    </div>
-                    <button @click.prevent="attachNewLink(file)" class="button">{{ trans('entities.attach') }}</button>
-
-                </div>
-            </div>
-
-        </div>
-
-        <div id="file-edit" v-if="fileToEdit" @keypress.enter.prevent="updateFile(fileToEdit)">
-            <h5>{{ trans('entities.attachments_edit_file') }}</h5>
-
-            <div class="form-group">
-                <label for="attachment-name-edit">{{ trans('entities.attachments_edit_file_name') }}</label>
-                <input type="text" id="attachment-name-edit" placeholder="{{ trans('entities.attachments_edit_file_name') }}" v-model="fileToEdit.name">
-                <p class="small text-neg" v-for="error in errors.edit.name" v-text="error"></p>
-            </div>
-
-            <div class="tab-container">
-                <div class="nav-tabs">
-                    <button type="button" @click="editTab = 'file'" :class="{selected: editTab === 'file'}" class="tab-item">{{ trans('entities.attachments_upload') }}</button>
-                    <button type="button" @click="editTab = 'link'" :class="{selected: editTab === 'link'}" class="tab-item">{{ trans('entities.attachments_set_link') }}</button>
-                </div>
-                <div v-if="editTab === 'file'">
-                    <dropzone :upload-url="getUploadUrl(fileToEdit)" :uploaded-to="pageId" placeholder="{{ trans('entities.attachments_edit_drop_upload') }}" @success="uploadSuccessUpdate"></dropzone>
-                    <br>
-                </div>
-                <div v-if="editTab === 'link'">
-                    <div class="form-group">
-                        <label for="attachment-link-edit">{{ trans('entities.attachments_link_url') }}</label>
-                        <input type="text" id="attachment-link-edit" placeholder="{{ trans('entities.attachment_link') }}" v-model="fileToEdit.link">
-                        <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">{{ trans('common.save') }}</button>
-        </div>
-
-    </div>
-</div>
\ No newline at end of file
index cfb66fdd0e34ad87827be468764670b230bc2a3b..5acd11af491c4c61ecb434cb08966378ddbd2b97 100644 (file)
@@ -20,8 +20,7 @@
         </form>
     </div>
     
-    @include('components.image-manager', ['imageType' => 'gallery', 'uploaded_to' => $page->id])
+    @include('components.image-manager', ['uploaded_to' => $page->id])
     @include('components.code-editor')
     @include('components.entity-selector-popup')
-
 @stop
\ No newline at end of file
index 6ea651820ef8f46f437f35a635b20712606847ce..87a9cc2de8bff90ba2036b0a312399cd41acbbb5 100644 (file)
     <div toolbox-tab-content="tags">
         <h4>{{ trans('entities.page_tags') }}</h4>
         <div class="px-l">
-            @include('components.tag-manager', ['entity' => $page, 'entityType' => 'page'])
+            @include('components.tag-manager', ['entity' => $page])
         </div>
     </div>
 
     @if(userCan('attachment-create-all'))
-        @include('pages.attachment-manager', ['page' => $page])
+        @include('attachments.manager', ['page' => $page])
     @endif
 
     <div toolbox-tab-content="templates">
index 4294ff56b79271369707abccfa7696433b4c2f6d..d153aed99af71e0598ecd4a7aa443a85e619159f 100644 (file)
@@ -1,22 +1,19 @@
-<div class="page-editor flex-fill flex" id="page-editor"
-     drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
+<div component="page-editor" class="page-editor flex-fill flex"
+     option:page-editor:drafts-enabled="{{ $draftsEnabled ? 'true' : 'false' }}"
      @if(config('services.drawio'))
         drawio-url="{{ is_string(config('services.drawio')) ? config('services.drawio') : 'https://www.draw.io/?embed=1&proto=json&spin=1' }}"
      @endif
-     editor-type="{{ setting('app-editor') }}"
-     page-id="{{ $model->id ?? 0 }}"
-     text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
-     page-new-draft="{{ $model->draft ?? 0 }}"
-     page-update-draft="{{ $model->isDraft ?? 0 }}">
-
-    @exposeTranslations([
-        'entities.pages_editing_draft',
-        'entities.pages_editing_page',
-        'errors.page_draft_autosave_fail',
-        'entities.pages_editing_page',
-        'entities.pages_draft_discarded',
-        'entities.pages_edit_set_changelog',
-    ])
+     @if($model->name === trans('entities.pages_initial_name'))
+        option:page-editor:has-default-title="true"
+     @endif
+     option:page-editor:editor-type="{{ setting('app-editor') }}"
+     option:page-editor:page-id="{{ $model->id ?? '0' }}"
+     option:page-editor:page-new-draft="{{ ($model->draft ?? false) ? 'true' : 'false' }}"
+     option:page-editor:draft-text="{{ ($model->draft || $model->isDraft) ? trans('entities.pages_editing_draft') : trans('entities.pages_editing_page') }}"
+     option:page-editor:autosave-fail-text="{{ trans('errors.page_draft_autosave_fail') }}"
+     option:page-editor:editing-page-text="{{ trans('entities.pages_editing_page') }}"
+     option:page-editor:draft-discarded-text="{{ trans('entities.pages_draft_discarded') }}"
+     option:page-editor:set-changelog-text="{{ trans('entities.pages_edit_set_changelog') }}">
 
     {{--Header Bar--}}
     <div class="primary-background-light toolbar page-edit-toolbar">
             </div>
 
             <div class="text-center px-m py-xs">
-                <div v-show="draftsEnabled" dropdown dropdown-move-menu class="dropdown-container draft-display text">
-                    <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span class="faded-text" v-text="draftText"></span>&nbsp; @icon('more')</button>
-                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', ':class' => '{visible: draftUpdated}'])
-                    <ul class="dropdown-menu" role="menu">
+                <div component="dropdown"
+                     option:dropdown:move-menu="true"
+                     class="dropdown-container draft-display text {{ $draftsEnabled ? '' : 'hidden' }}">
+                    <button type="button" refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" title="{{ trans('entities.pages_edit_draft_options') }}" class="text-primary text-button"><span refs="page-editor@draftDisplay" class="faded-text"></span>&nbsp; @icon('more')</button>
+                    @icon('check-circle', ['class' => 'text-pos draft-notification svg-icon', 'refs' => 'page-editor@draftDisplayIcon'])
+                    <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                         <li>
-                            <button type="button" @click="saveDraft()" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
+                            <button refs="page-editor@saveDraft" type="button" class="text-pos">@icon('save'){{ trans('entities.pages_edit_save_draft') }}</button>
                         </li>
-                        <li v-if="isNewDraft">
+                        @if ($model->draft)
+                        <li>
                             <a href="{{ $model->getUrl('/delete') }}" class="text-neg">@icon('delete'){{ trans('entities.pages_edit_delete_draft') }}</a>
                         </li>
-                        <li v-if="isUpdateDraft">
-                            <button type="button" @click="discardDraft" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
+                        @endif
+                        <li refs="page-editor@discardDraftWrap" class="{{ ($model->isDraft ?? false) ? '' : 'hidden' }}">
+                            <button refs="page-editor@discardDraft" type="button" class="text-neg">@icon('cancel'){{ trans('entities.pages_edit_discard_draft') }}</button>
                         </li>
                     </ul>
                 </div>
             </div>
 
-            <div class="action-buttons px-m py-xs" v-cloak>
-                <div dropdown dropdown-move-menu class="dropdown-container">
-                    <button type="button" dropdown-toggle aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span v-text="changeSummaryShort"></span></button>
-                    <ul class="wide dropdown-menu">
+            <div class="action-buttons px-m py-xs">
+                <div component="dropdown" dropdown-move-menu class="dropdown-container">
+                    <button refs="dropdown@toggle" type="button" aria-haspopup="true" aria-expanded="false" class="text-primary text-button">@icon('edit') <span refs="page-editor@changelogDisplay">{{ trans('entities.pages_edit_set_changelog') }}</span></button>
+                    <ul refs="dropdown@menu" class="wide dropdown-menu">
                         <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" />
+                            <input refs="page-editor@changelogInput"
+                                   name="summary"
+                                   id="summary-input"
+                                   type="text"
+                                   placeholder="{{ trans('entities.pages_edit_enter_changelog') }}" />
                         </li>
                     </ul>
                     <span>{{-- Prevents button jumping on menu show --}}</span>
@@ -62,8 +67,8 @@
     </div>
 
     {{--Title input--}}
-    <div class="title-input page-title clearfix" v-pre>
-        <div class="input" @if($model->name === trans('entities.pages_initial_name')) is-default-value @endif>
+    <div class="title-input page-title clearfix">
+        <div refs="page-editor@titleContainer" class="input">
             @include('form.text', ['name' => 'name', 'model' => $model, 'placeholder' => trans('entities.pages_title')])
         </div>
     </div>
@@ -83,5 +88,8 @@
 
     </div>
 
-    <button type="submit" id="save-button-mobile" title="{{ trans('entities.pages_save') }}" class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
+    <button type="submit"
+            id="save-button-mobile"
+            title="{{ trans('entities.pages_save') }}"
+            class="text-primary text-button hide-over-m page-save-mobile-button">@icon('save')</button>
 </div>
\ No newline at end of file
index 85afbea069ab4726c6588971445e439d65ebb5d2..a004dbd8be7024226d12d8b7d9b68c37a79b6359 100644 (file)
@@ -1,4 +1,7 @@
-<div v-pre id="markdown-editor" markdown-editor class="flex-fill flex code-fill">
+<div id="markdown-editor" component="markdown-editor"
+     option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
+     option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     class="flex-fill flex code-fill">
     @exposeTranslations([
         'errors.image_upload_error',
     ])
index e13632c1ec86a41b3c1818be59add6b46682679f..ba2a2c336fd60cd0fce5b8761d176285f207098d 100644 (file)
@@ -1,6 +1,6 @@
 <div dir="auto">
 
-    <h1 class="break-text" v-pre id="bkmrk-page-title">{{$page->name}}</h1>
+    <h1 class="break-text" id="bkmrk-page-title">{{$page->name}}</h1>
 
     <div style="clear:left;"></div>
 
index feb3180775adaff018fe9dd739e21bc64354998c..6ff33c68de4b7e2baddc4db1301f111a0161c865 100644 (file)
@@ -50,9 +50,9 @@
                                 @else
                                     <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div dropdown class="dropdown-container">
-                                        <a dropdown-toggle href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
-                                        <ul class="dropdown-menu" role="menu">
+                                    <div component="dropdown" class="dropdown-container">
+                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
+                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                                             <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">
@@ -64,9 +64,9 @@
                                         </ul>
                                     </div>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
-                                    <div dropdown class="dropdown-container">
-                                        <a dropdown-toggle href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
-                                        <ul class="dropdown-menu" role="menu">
+                                    <div component="dropdown" class="dropdown-container">
+                                        <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('common.delete') }}</a>
+                                        <ul refs="dropdown@menu" class="dropdown-menu" role="menu">
                                             <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">
index cfa5ee9ce08572c3225a4223f5d6ac24e3e257d6..48c88434e00bafd25cff7befb7e39e95f0f475e1 100644 (file)
         <div id="page-attachments" class="mb-l">
             <h5>{{ trans('entities.pages_attachments') }}</h5>
             <div class="body">
-                @foreach($page->attachments as $attachment)
-                    <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
+                @include('attachments.list', ['attachments' => $page->attachments])
             </div>
         </div>
     @endif
index 1a67ee76f2d0992f7eb552878359f6d151db3b51..3bc85caa9d57a749128fad5f3cb9de02f57035d9 100644 (file)
@@ -1,10 +1,13 @@
-<div wysiwyg-editor class="flex-fill flex">
+<div component="wysiwyg-editor"
+     option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
+     option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
+     class="flex-fill flex">
 
     @exposeTranslations([
         'errors.image_upload_error',
     ])
 
-    <textarea id="html-editor"  name="html" rows="5" v-pre
+    <textarea id="html-editor"  name="html" rows="5"
           @if($errors->has('html')) class="text-neg" @endif>@if(isset($model) || old('html')){{ old('html') ? old('html') : $model->html }}@endif</textarea>
 </div>
 
index 39fb35fe2728c4c50c2ee5392414f9dfdee0a5c8..4fd8dde1b58550fbf1f0f6c59c84ead73f88c11e 100644 (file)
@@ -7,7 +7,7 @@
     @endif
 </div>
 
-<div v-pre>
+<div>
     @if($activity->user)
         <a href="{{ $activity->user->getProfileUrl() }}">{{ $activity->user->name }}</a>
     @else
index c288e63674ff38b456a4b13ed530746510f11e29..5131af1aa36ae5728f12cd6c52e429e53aeaadd2 100644 (file)
@@ -1,4 +1,7 @@
-<nav id="book-tree" class="book-tree mb-xl" v-pre aria-label="{{ trans('entities.books_navigation') }}">
+<nav id="book-tree"
+     class="book-tree mb-xl"
+     aria-label="{{ trans('entities.books_navigation') }}">
+
     <h5>{{ trans('entities.books_navigation') }}</h5>
 
     <ul class="sidebar-page-list mt-xs menu entity-list">
index e53cb4c53b9cf59200427f67d6134d90ce1c5bbd..a1a33ae1c7764e932c10912ccfb70af3d6c276d4 100644 (file)
@@ -1,9 +1,9 @@
-<div class="breadcrumb-listing" dropdown breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
-    <div class="breadcrumb-listing-toggle" dropdown-toggle
+<div class="breadcrumb-listing" component="dropdown" breadcrumb-listing="{{ $entity->getType() }}:{{ $entity->id }}">
+    <div class="breadcrumb-listing-toggle" refs="dropdown@toggle"
          aria-haspopup="true" aria-expanded="false" tabindex="0">
         <div class="separator">@icon('chevron-right')</div>
     </div>
-    <div dropdown-menu class="breadcrumb-listing-dropdown card" role="menu">
+    <div refs="dropdown@menu" class="breadcrumb-listing-dropdown card" role="menu">
         <div class="breadcrumb-listing-search">
             @icon('search')
             <input autocomplete="off" type="text" name="entity-search" placeholder="{{ trans('common.search') }}" aria-label="{{ trans('common.search') }}">
diff --git a/resources/views/partials/entity-dashboard-search-box.blade.php b/resources/views/partials/entity-dashboard-search-box.blade.php
deleted file mode 100644 (file)
index 2e03952..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-<div class="mb-xl">
-    <form v-on:submit.prevent="searchBook" class="search-box flexible" role="search">
-        <input v-model="searchTerm" v-on:change="checkSearchForm" type="text" aria-label="{{ trans('entities.books_search_this') }}" name="term" placeholder="{{ trans('entities.books_search_this') }}">
-        <button type="submit" aria-label="{{ trans('common.search') }}">@icon('search')</button>
-        <button v-if="searching" v-cloak class="search-box-cancel text-neg" v-on:click="clearSearch"
-                type="button" aria-label="{{ trans('common.search_clear') }}">@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
deleted file mode 100644 (file)
index 68c6f53..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-<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
index 630d640bf3214fe8b44fa02801bf94b11661939c..4d847bcaef801c4700a74a84de8fc7a025fc8fe6 100644 (file)
@@ -1,10 +1,10 @@
-<div dropdown class="dropdown-container" id="export-menu">
-    <div dropdown-toggle class="icon-list-item"
+<div component="dropdown" class="dropdown-container" id="export-menu">
+    <div refs="dropdown@toggle" class="icon-list-item"
          aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('entities.export') }}" tabindex="0">
         <span>@icon('export')</span>
         <span>{{ trans('entities.export') }}</span>
     </div>
-    <ul class="wide dropdown-menu" role="menu">
+    <ul refs="dropdown@menu" class="wide dropdown-menu" role="menu">
         <li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
         <li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
         <li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
diff --git a/resources/views/partials/entity-search-form.blade.php b/resources/views/partials/entity-search-form.blade.php
new file mode 100644 (file)
index 0000000..b65d5df
--- /dev/null
@@ -0,0 +1,10 @@
+{{--
+@label - Placeholder/aria-label text
+--}}
+<div class="mb-xl">
+    <form refs="entity-search@searchForm" class="search-box flexible" role="search">
+        <input refs="entity-search@searchInput" type="text"
+               aria-label="{{ $label }}" name="term" placeholder="{{ $label }}">
+        <button type="submit" aria-label="{{ trans('common.search') }}">@icon('search')</button>
+    </form>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-search-results.blade.php b/resources/views/partials/entity-search-results.blade.php
new file mode 100644 (file)
index 0000000..7461983
--- /dev/null
@@ -0,0 +1,15 @@
+<div refs="entity-search@searchView" class="search-results hidden">
+    <div class="grid half v-center">
+        <h3 class="text-muted px-none">
+            {{ trans('entities.search_results') }}
+        </h3>
+        <div class="text-right">
+            <a refs="entity-search@clearButton" class="button outline">{{ trans('entities.search_clear') }}</a>
+        </div>
+    </div>
+
+    <div refs="entity-search@loadingBlock">
+        @include('partials.loading-icon')
+    </div>
+    <div class="book-contents" refs="entity-search@searchResults"></div>
+</div>
\ No newline at end of file
index 09c61d01383fa01f2d5e6f0c1af54b586dff75c7..af0981800049322a9062eef2ea81ecf9a9898354 100644 (file)
@@ -12,9 +12,9 @@
         <input type="hidden" value="{{ $order }}" name="order">
 
         <div class="list-sort">
-            <div class="list-sort-type dropdown-container" dropdown>
-                <div dropdown-toggle aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" tabindex="0">{{ $options[$selectedSort] }}</div>
-                <ul class="dropdown-menu">
+            <div component="dropdown" class="list-sort-type dropdown-container">
+                <div refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" tabindex="0">{{ $options[$selectedSort] }}</div>
+                <ul refs="dropdown@menu" class="dropdown-menu">
                     @foreach($options as $key => $label)
                         <li @if($key === $selectedSort) class="active" @endif><a href="#" data-sort-value="{{$key}}">{{ $label }}</a></li>
                     @endforeach
index f19e560a2d5f633b01bf063c074d5396a5819419..df137bd2a3bcbef73662ff1355b921df8d76f14c 100644 (file)
 @extends('simple-layout')
 
 @section('body')
-    <input type="hidden" name="searchTerm" value="{{$searchTerm}}">
-
-    <div class="container" id="search-system">
-
-        <div class="my-s">
-            &nbsp;
-        </div>
+    <div class="container mt-xl" id="search-system">
 
         <div class="grid right-focus reverse-collapse gap-xl">
             <div>
                 <div>
-                    <h5>{{ trans('entities.search_filters') }}</h5>
+                    <h5>{{ trans('entities.search_advanced') }}</h5>
 
-                    <form v-on:submit="updateSearch" v-cloak class="v-cloak anim fadeIn">
-                        <h6 class="text-muted">{{ trans('entities.search_content_type') }}</h6>
+                    <form method="get" action="{{ url('/search') }}">
+                        <h6>{{ trans('entities.search_terms') }}</h6>
+                        <input type="text" name="search" value="{{ implode(' ', $options->searches) }}">
+
+                        <h6>{{ 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>
+
+                            <?php
+                            $types = explode('|', $options->filters['type'] ?? '');
+                            $hasTypes = $types[0] !== '';
+                            ?>
+                            @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('page', $types), 'entity' => 'page', 'transKey' => 'page'])
+                            @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('chapter', $types), 'entity' => 'chapter', 'transKey' => 'chapter'])
                             <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>
+                                @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('book', $types), 'entity' => 'book', 'transKey' => 'book'])
+                                @include('search.form.type-filter', ['checked' => !$hasTypes || in_array('bookshelf', $types), 'entity' => 'bookshelf', 'transKey' => 'shelf'])
                         </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>
+                        <h6>{{ trans('entities.search_exact_matches') }}</h6>
+                        @include('search.form.term-list', ['type' => 'exact', 'currentList' => $options->exacts])
+
+                        <h6>{{ trans('entities.search_tags') }}</h6>
+                        @include('search.form.term-list', ['type' => 'tags', 'currentList' => $options->tags])
 
                         @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">
+                            <h6>{{ trans('entities.search_options') }}</h6>
+
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'viewed_by_me', 'value' => null])
                                 {{ 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">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'not_viewed_by_me', 'value' => null])
                                 {{ 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">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'is_restricted', 'value' => null])
                                 {{ 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">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'created_by', 'value' => 'me'])
                                 {{ 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">
+                            @endcomponent
+                            @component('search.form.boolean-filter', ['filters' => $options->filters, 'name' => 'updated_by', 'value' => 'me'])
                                 {{ trans('entities.search_updated_by_me') }}
-                            </label>
+                            @endcomponent
                         @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>
-
+                        <h6>{{ trans('entities.search_date_options') }}</h6>
+                        @include('search.form.date-filter', ['name' => 'updated_after', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'updated_before', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'created_after', 'filters' => $options->filters])
+                        @include('search.form.date-filter', ['name' => 'created_before', 'filters' => $options->filters])
 
                         <button type="submit" class="button">{{ trans('entities.search_update') }}</button>
                     </form>
                 </div>
             </div>
             <div>
-                <div v-pre class="card content-wrap">
+                <div class="card content-wrap">
                     <h1 class="list-heading">{{ trans('entities.search_results') }}</h1>
+
                     <form action="{{ url('/search') }}" method="GET"  class="search-box flexible hide-over-l">
                         <input value="{{$searchTerm}}" type="text" name="term" placeholder="{{ trans('common.search') }}">
                         <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>
+
                     <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>
diff --git a/resources/views/search/form/boolean-filter.blade.php b/resources/views/search/form/boolean-filter.blade.php
new file mode 100644 (file)
index 0000000..1dc9bf0
--- /dev/null
@@ -0,0 +1,12 @@
+{{--
+$filters - Array of search filter values
+$name - Name of filter to limit use.
+$value - Value of filter to use
+--}}
+<label class="checkbox">
+    <input type="checkbox"
+           name="filters[{{ $name }}]"
+           @if (isset($filters[$name]) && (!$value || ($value && $value === $filters[$name]))) checked="checked" @endif
+           value="{{ $value ?: 'true' }}">
+    {{ $slot }}
+</label>
\ No newline at end of file
diff --git a/resources/views/search/form/date-filter.blade.php b/resources/views/search/form/date-filter.blade.php
new file mode 100644 (file)
index 0000000..05ab4c1
--- /dev/null
@@ -0,0 +1,29 @@
+{{--
+@filters - Active search filters
+@name - Name of filter
+--}}
+<table class="no-style form-table mb-xs">
+    <tr>
+        <td width="200">{{ trans('entities.search_' . $name) }}</td>
+        <td width="80"></td>
+    </tr>
+    <tr component="optional-input">
+        <td>
+            <button type="button" refs="optional-input@show"
+                    class="text-button {{ ($filters[$name] ?? false) ? 'hidden' : '' }}">{{ trans('entities.search_set_date') }}</button>
+            <input class="tag-input {{ ($filters[$name] ?? false) ? '' : 'hidden' }}"
+                   refs="optional-input@input"
+                   value="{{ $filters[$name] ?? '' }}"
+                   type="date"
+                   name="filters[{{ $name }}]"
+                   pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}">
+        </td>
+        <td>
+            <button type="button"
+                    refs="optional-input@remove"
+                    class="text-neg text-button {{ ($filters[$name] ?? false) ? '' : 'hidden' }}">
+                @icon('close')
+            </button>
+        </td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/resources/views/search/form/term-list.blade.php b/resources/views/search/form/term-list.blade.php
new file mode 100644 (file)
index 0000000..3fbfa18
--- /dev/null
@@ -0,0 +1,26 @@
+{{--
+@type - Type of term (exact, tag)
+@currentList
+--}}
+<table component="add-remove-rows"
+       option:add-remove-rows:remove-selector="button.text-neg"
+       option:add-remove-rows:row-selector="tr"
+       class="no-style">
+    @foreach(array_merge($currentList, ['']) as $term)
+        <tr @if(empty($term)) class="hidden" refs="add-remove-rows@model" @endif>
+            <td class="pb-s pr-m">
+                <input class="exact-input outline" type="text" name="{{$type}}[]" value="{{ $term }}">
+            </td>
+            <td>
+                <button type="button" class="text-neg text-button">@icon('close')</button>
+            </td>
+        </tr>
+    @endforeach
+    <tr>
+        <td colspan="2">
+            <button refs="add-remove-rows@add" type="button" class="text-button">
+                @icon('add-circle'){{ trans('common.add') }}
+            </button>
+        </td>
+    </tr>
+</table>
\ No newline at end of file
diff --git a/resources/views/search/form/type-filter.blade.php b/resources/views/search/form/type-filter.blade.php
new file mode 100644 (file)
index 0000000..b885ebd
--- /dev/null
@@ -0,0 +1,10 @@
+{{--
+@checked - If the option should be pre-checked
+@entity - Entity Name
+@transKey - Translation Key
+--}}
+<label class="inline checkbox text-{{$entity}}">
+    <input type="checkbox" name="types[]"
+           @if($checked) checked @endif
+           value="{{$entity}}">{{ trans('entities.' . $transKey) }}
+</label>
\ No newline at end of file
diff --git a/resources/views/settings/audit.blade.php b/resources/views/settings/audit.blade.php
new file mode 100644 (file)
index 0000000..9b97f06
--- /dev/null
@@ -0,0 +1,98 @@
+@extends('simple-layout')
+
+@section('body')
+<div class="container">
+
+    <div class="grid left-focus v-center no-row-gap">
+        <div class="py-m">
+            @include('settings.navbar', ['selected' => 'audit'])
+        </div>
+    </div>
+
+    <div class="card content-wrap auto-height">
+        <h2 class="list-heading">{{ trans('settings.audit') }}</h2>
+        <p class="text-muted">{{ trans('settings.audit_desc') }}</p>
+
+        <div class="flex-container-row">
+            <div component="dropdown" class="list-sort-type dropdown-container mr-m">
+                <label for="">{{ trans('settings.audit_event_filter') }}</label>
+                <button refs="dropdown@toggle" aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('common.sort_options') }}" class="input-base text-left">{{ $listDetails['event'] ?: trans('settings.audit_event_filter_no_filter') }}</button>
+                <ul refs="dropdown@menu" class="dropdown-menu">
+                    <li @if($listDetails['event'] === '') class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => '']) }}">{{ trans('settings.audit_event_filter_no_filter') }}</a></li>
+                    @foreach($activityKeys as $key)
+                        <li @if($key === $listDetails['event']) class="active" @endif><a href="{{ sortUrl('/settings/audit', $listDetails, ['event' => $key]) }}">{{ $key }}</a></li>
+                    @endforeach
+                </ul>
+            </div>
+
+            @foreach(['date_from', 'date_to'] as $filterKey)
+                <form action="{{ url('/settings/audit') }}" method="get" class="block mr-m">
+                    @foreach($listDetails as $param => $val)
+                        @if(!empty($val) && $param !== $filterKey)
+                            <input type="hidden" name="{{ $param }}" value="{{ $val }}">
+                        @endif
+                    @endforeach
+                    <label for="audit_filter_{{ $filterKey }}">{{ trans('settings.audit_' . $filterKey) }}</label>
+                    <input id="audit_filter_{{ $filterKey }}"
+                           component="submit-on-change"
+                           type="date"
+                           name="{{ $filterKey }}"
+                           value="{{ $listDetails[$filterKey] ?? '' }}">
+                </form>
+            @endforeach
+        </div>
+
+        <hr class="mt-l mb-s">
+
+        {{ $activities->links() }}
+
+        <table class="table">
+            <tbody>
+            <tr>
+                <th>{{ trans('settings.audit_table_user') }}</th>
+                <th>
+                    <a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'key']) }}">{{ trans('settings.audit_table_event') }}</a>
+                </th>
+                <th>{{ trans('settings.audit_table_item') }}</th>
+                <th>
+                    <a href="{{ sortUrl('/settings/audit', $listDetails, ['sort' => 'created_at']) }}">{{ trans('settings.audit_table_date') }}</a></th>
+            </tr>
+            @foreach($activities as $activity)
+                <tr>
+                    <td>
+                        @if($activity->user)
+                            <a href="{{ $activity->user->getEditUrl() }}" class="audit-log-user">
+                                <div><img class="avatar block" src="{{ $activity->user->getAvatar(40)}}" alt="{{ $activity->user->name }}"></div>
+                                <div>{{ $activity->user->name }}</div>
+                            </a>
+                        @else
+                            [ID: {{ $activity->user_id }}] {{ trans('common.deleted_user') }}
+                        @endif
+                    </td>
+                    <td>{{ $activity->key }}</td>
+                    <td>
+                        @if($activity->entity)
+                            <a href="{{ $activity->entity->getUrl() }}" class="icon-list-item">
+                                <span role="presentation" class="icon text-{{$activity->entity->getType()}}">@icon($activity->entity->getType())</span>
+                                <div class="text-{{ $activity->entity->getType() }}">
+                                    {{ $activity->entity->name }}
+                                </div>
+                            </a>
+                        @elseif($activity->extra)
+                            <div class="px-m">
+                                {{ trans('settings.audit_deleted_item') }} <br>
+                                {{ trans('settings.audit_deleted_item_name', ['name' => $activity->extra]) }}
+                            </div>
+                        @endif
+                    </td>
+                    <td>{{ $activity->created_at }}</td>
+                </tr>
+            @endforeach
+            </tbody>
+        </table>
+
+        {{ $activities->links() }}
+    </div>
+
+</div>
+@stop
index 6ccb8d8f9d1d50aabf1290991aaccdcab9f2858b..b688d96469a7df900be90cc1587f20192aec3478 100644 (file)
                             <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)
-                                    <option value="{{$role->id}}" data-role-name="{{ $role->name }}"
+                                    <option value="{{$role->id}}"
+                                            data-system-role-name="{{ $role->system_name ?? '' }}"
                                             @if(setting('registration-role', \BookStack\Auth\Role::first()->id) == $role->id) selected @endif
                                     >
                                         {{ $role->display_name }}
 
     </div>
 
-    @include('components.image-manager', ['imageType' => 'system'])
     @include('components.entity-selector-popup', ['entityTypes' => 'page'])
 @stop
index 896de9d97477c0c3e510ca806ed7e33cbf12e611..af8b2aaf7ee3b134e915511760a33d456905389f 100644 (file)
@@ -4,6 +4,9 @@
         <a href="{{ url('/settings') }}" @if($selected == 'settings') class="active" @endif>@icon('settings'){{ trans('settings.settings') }}</a>
         <a href="{{ url('/settings/maintenance') }}" @if($selected == 'maintenance') class="active" @endif>@icon('spanner'){{ trans('settings.maint') }}</a>
     @endif
+    @if($currentUser->can('settings-manage') && $currentUser->can('users-manage'))
+        <a href="{{ url('/settings/audit') }}" @if($selected == 'audit') class="active" @endif>@icon('open-book'){{ trans('settings.audit') }}</a>
+    @endif
     @if($currentUser->can('users-manage'))
         <a href="{{ url('/settings/users') }}" @if($selected == 'users') class="active" @endif>@icon('users'){{ trans('settings.users') }}</a>
     @endif
index 4f40345df99947dec5de11fa2d44cf88dcf382e0..fa7c12b0a2aafab0ec401e4a951a24966a89afa6 100644 (file)
@@ -19,7 +19,7 @@
                 @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'])
+                        @include('form.role-select', ['options' => $roles, 'name' => 'migrate_role_id'])
                     </div>
                 @endif
 
index ed57ad94401feb18417fc72a6b843ea1095c287e..43bc2b0242e1f7d0e4fe14a97dad5b3448f508d3 100644 (file)
             </div>
         </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="toggle-switch-list">
-                <div>@include('settings.roles.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
-                <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' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])</div>
-                <div>@include('settings.roles.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])</div>
+        <div permissions-table>
+            <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 class="toggle-switch-list grid half mt-m">
+                <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' => 'templates-manage', 'label' => trans('settings.role_manage_page_templates')])</div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'access-api', 'label' => trans('settings.role_access_api')])</div>
+                </div>
+                <div>
+                    <div>@include('settings.roles.checkbox', ['permission' => 'settings-manage', 'label' => trans('settings.role_manage_settings')])</div>
+                    <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>
+                    <p class="text-warn text-small mt-s mb-none">{{ trans('settings.roles_system_warning') }}</p>
+                </div>
             </div>
         </div>
 
index 19c5bbecd69ae95c70bd782703cfe377ddd32b30..e635455bfd93a359ae2d3352b716fd9dc399085d 100644 (file)
@@ -60,7 +60,7 @@
         <label for="tag-manager">{{ trans('entities.shelf_tags') }}</label>
     </button>
     <div class="collapse-content" collapsible-content>
-        @include('components.tag-manager', ['entity' => $shelf ?? null, 'entityType' => 'bookshelf'])
+        @include('components.tag-manager', ['entity' => $shelf ?? null])
     </div>
 </div>
 
index f9c27b62f29581770b65c0a704ebaf31a8f66324..1b90d9b8fd12d591cf87b6e34fba2191b6711ff9 100644 (file)
@@ -9,18 +9,28 @@
 Route::get('docs', 'ApiDocsController@display');
 Route::get('docs.json', 'ApiDocsController@json');
 
-Route::get('books', 'BooksApiController@list');
-Route::post('books', 'BooksApiController@create');
-Route::get('books/{id}', 'BooksApiController@read');
-Route::put('books/{id}', 'BooksApiController@update');
-Route::delete('books/{id}', 'BooksApiController@delete');
+Route::get('books', 'BookApiController@list');
+Route::post('books', 'BookApiController@create');
+Route::get('books/{id}', 'BookApiController@read');
+Route::put('books/{id}', 'BookApiController@update');
+Route::delete('books/{id}', 'BookApiController@delete');
 
-Route::get('books/{id}/export/html', 'BooksExportApiController@exportHtml');
-Route::get('books/{id}/export/pdf', 'BooksExportApiController@exportPdf');
-Route::get('books/{id}/export/plaintext', 'BooksExportApiController@exportPlainText');
+Route::get('books/{id}/export/html', 'BookExportApiController@exportHtml');
+Route::get('books/{id}/export/pdf', 'BookExportApiController@exportPdf');
+Route::get('books/{id}/export/plaintext', 'BookExportApiController@exportPlainText');
+
+Route::get('chapters', 'ChapterApiController@list');
+Route::post('chapters', 'ChapterApiController@create');
+Route::get('chapters/{id}', 'ChapterApiController@read');
+Route::put('chapters/{id}', 'ChapterApiController@update');
+Route::delete('chapters/{id}', 'ChapterApiController@delete');
+
+Route::get('chapters/{id}/export/html', 'ChapterExportApiController@exportHtml');
+Route::get('chapters/{id}/export/pdf', 'ChapterExportApiController@exportPdf');
+Route::get('chapters/{id}/export/plaintext', 'ChapterExportApiController@exportPlainText');
 
 Route::get('shelves', 'BookshelfApiController@list');
 Route::post('shelves', 'BookshelfApiController@create');
 Route::get('shelves/{id}', 'BookshelfApiController@read');
 Route::put('shelves/{id}', 'BookshelfApiController@update');
-Route::delete('shelves/{id}', 'BookshelfApiController@delete');
\ No newline at end of file
+Route::delete('shelves/{id}', 'BookshelfApiController@delete');
index 3e05e394d29698498e19f2ec739e17dd0d1a4a1c..acbcb4e8fd4eb8f8e78002adc51584ec79306f02 100644 (file)
@@ -101,22 +101,14 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/user/{userId}', 'UserController@showProfilePage');
 
     // Image routes
-    Route::group(['prefix' => 'images'], function () {
-
-        // Gallery
-        Route::get('/gallery', 'Images\GalleryImageController@list');
-        Route::post('/gallery', 'Images\GalleryImageController@create');
-
-        // Drawio
-        Route::get('/drawio', 'Images\DrawioImageController@list');
-        Route::get('/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
-        Route::post('/drawio', 'Images\DrawioImageController@create');
-
-        // Shared gallery & draw.io endpoint
-        Route::get('/usage/{id}', 'Images\ImageController@usage');
-        Route::put('/{id}', 'Images\ImageController@update');
-        Route::delete('/{id}', 'Images\ImageController@destroy');
-    });
+    Route::get('/images/gallery', 'Images\GalleryImageController@list');
+    Route::post('/images/gallery', 'Images\GalleryImageController@create');
+    Route::get('/images/drawio', 'Images\DrawioImageController@list');
+    Route::get('/images/drawio/base64/{id}', 'Images\DrawioImageController@getAsBase64');
+    Route::post('/images/drawio', 'Images\DrawioImageController@create');
+    Route::get('/images/edit/{id}', 'Images\ImageController@edit');
+    Route::put('/images/{id}', 'Images\ImageController@update');
+    Route::delete('/images/{id}', 'Images\ImageController@destroy');
 
     // Attachments routes
     Route::get('/attachments/{id}', 'AttachmentController@get');
@@ -124,6 +116,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::post('/attachments/upload/{id}', 'AttachmentController@uploadUpdate');
     Route::post('/attachments/link', 'AttachmentController@attachLink');
     Route::put('/attachments/{id}', 'AttachmentController@update');
+    Route::get('/attachments/edit/{id}', 'AttachmentController@getUpdateForm');
     Route::get('/attachments/get/page/{pageId}', 'AttachmentController@listForPage');
     Route::put('/attachments/sort/page/{pageId}', 'AttachmentController@sortForPage');
     Route::delete('/attachments/{id}', 'AttachmentController@delete');
@@ -134,8 +127,7 @@ Route::group(['middleware' => 'auth'], function () {
     Route::delete('/ajax/page/{id}', 'PageController@ajaxDestroy');
 
     // Tag routes (AJAX)
-    Route::group(['prefix' => 'ajax/tags'], function() {
-        Route::get('/get/{entityType}/{entityId}', 'TagController@getForEntity');
+    Route::group(['prefix' => 'ajax/tags'], function () {
         Route::get('/suggest/names', 'TagController@getNameSuggestions');
         Route::get('/suggest/values', 'TagController@getValueSuggestions');
     });
@@ -143,9 +135,9 @@ Route::group(['middleware' => 'auth'], function () {
     Route::get('/ajax/search/entities', 'SearchController@searchEntitiesAjax');
 
     // Comments
-    Route::post('/ajax/page/{pageId}/comment', 'CommentController@savePageComment');
-    Route::put('/ajax/comment/{id}', 'CommentController@update');
-    Route::delete('/ajax/comment/{id}', 'CommentController@destroy');
+    Route::post('/comment/{pageId}', 'CommentController@savePageComment');
+    Route::put('/comment/{id}', 'CommentController@update');
+    Route::delete('/comment/{id}', 'CommentController@destroy');
 
     // Links
     Route::get('/link/{id}', 'PageController@redirectFromLink');
@@ -170,9 +162,12 @@ Route::group(['middleware' => 'auth'], function () {
         Route::post('/', 'SettingController@update');
 
         // Maintenance
-        Route::get('/maintenance', 'SettingController@showMaintenance');
-        Route::delete('/maintenance/cleanup-images', 'SettingController@cleanupImages');
-        Route::post('/maintenance/send-test-email', 'SettingController@sendTestEmail');
+        Route::get('/maintenance', 'MaintenanceController@index');
+        Route::delete('/maintenance/cleanup-images', 'MaintenanceController@cleanupImages');
+        Route::post('/maintenance/send-test-email', 'MaintenanceController@sendTestEmail');
+
+        // Audit Log
+        Route::get('/audit', 'AuditLogController@index');
 
         // Users
         Route::get('/users', 'UserController@index');
index 3cbcadfa30759d4197bbc8775370b0b954760590..1687c64a17e10a7a5110166d251be7c2721afcf1 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Api;
 
+use BookStack\Auth\User;
 use Tests\TestCase;
 
 class ApiDocsTest extends TestCase
@@ -39,4 +40,19 @@ class ApiDocsTest extends TestCase
             ] ]
         ]);
     }
+
+    public function test_docs_page_visible_by_public_user_if_given_permission()
+    {
+        $this->setSettings(['app-public' => true]);
+        $guest = User::getDefault();
+
+        $this->startSession();
+        $resp = $this->get('/api/docs');
+        $resp->assertStatus(403);
+
+        $this->giveUserPermissions($guest, ['access-api']);
+
+        $resp = $this->get('/api/docs');
+        $resp->assertStatus(200);
+    }
 }
\ No newline at end of file
diff --git a/tests/Api/ChaptersApiTest.php b/tests/Api/ChaptersApiTest.php
new file mode 100644 (file)
index 0000000..15a4445
--- /dev/null
@@ -0,0 +1,186 @@
+<?php namespace Tests\Api;
+
+use BookStack\Entities\Book;
+use BookStack\Entities\Chapter;
+use Tests\TestCase;
+
+class ChaptersApiTest extends TestCase
+{
+    use TestsApi;
+
+    protected $baseEndpoint = '/api/chapters';
+
+    public function test_index_endpoint_returns_expected_chapter()
+    {
+        $this->actingAsApiEditor();
+        $firstChapter = Chapter::query()->orderBy('id', 'asc')->first();
+
+        $resp = $this->getJson($this->baseEndpoint . '?count=1&sort=+id');
+        $resp->assertJson(['data' => [
+            [
+                'id' => $firstChapter->id,
+                'name' => $firstChapter->name,
+                'slug' => $firstChapter->slug,
+                'book_id' => $firstChapter->book->id,
+                'priority' => $firstChapter->priority,
+            ]
+        ]]);
+    }
+
+    public function test_create_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'name' => 'My API chapter',
+            'description' => 'A chapter created via the API',
+            'book_id' => $book->id,
+            'tags' => [
+                [
+                    'name' => 'tagname',
+                    'value' => 'tagvalue',
+                ]
+            ]
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(200);
+        $newItem = Chapter::query()->orderByDesc('id')->where('name', '=', $details['name'])->first();
+        $resp->assertJson(array_merge($details, ['id' => $newItem->id, 'slug' => $newItem->slug]));
+        $this->assertDatabaseHas('tags', [
+            'entity_id' => $newItem->id,
+            'entity_type' => $newItem->getMorphClass(),
+            'name' => 'tagname',
+            'value' => 'tagvalue',
+        ]);
+        $resp->assertJsonMissing(['pages' => []]);
+        $this->assertActivityExists('chapter_create', $newItem);
+    }
+
+    public function test_chapter_name_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::query()->first();
+        $details = [
+            'book_id' => $book->id,
+            'description' => 'A chapter created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "name" => ["The name field is required."]
+        ]));
+    }
+
+    public function test_chapter_book_id_needed_to_create()
+    {
+        $this->actingAsApiEditor();
+        $details = [
+            'name' => 'My api chapter',
+            'description' => 'A chapter created via the API',
+        ];
+
+        $resp = $this->postJson($this->baseEndpoint, $details);
+        $resp->assertStatus(422);
+        $resp->assertJson($this->validationResponse([
+            "book_id" => ["The book id field is required."]
+        ]));
+    }
+
+    public function test_read_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $page = $chapter->pages()->first();
+
+        $resp = $this->getJson($this->baseEndpoint . "/{$chapter->id}");
+        $resp->assertStatus(200);
+        $resp->assertJson([
+            'id' => $chapter->id,
+            'slug' => $chapter->slug,
+            'created_by' => [
+                'name' => $chapter->createdBy->name,
+            ],
+            'book_id' => $chapter->book_id,
+            'updated_by' => [
+                'name' => $chapter->createdBy->name,
+            ],
+            'pages' => [
+                [
+                    'id' => $page->id,
+                    'slug' => $page->slug,
+                    'name' => $page->name,
+                ]
+            ],
+        ]);
+        $resp->assertJsonCount($chapter->pages()->count(), 'pages');
+    }
+
+    public function test_update_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $details = [
+            'name' => 'My updated API chapter',
+            'description' => 'A chapter created via the API',
+            'tags' => [
+                [
+                    'name' => 'freshtag',
+                    'value' => 'freshtagval',
+                ]
+            ],
+        ];
+
+        $resp = $this->putJson($this->baseEndpoint . "/{$chapter->id}", $details);
+        $chapter->refresh();
+
+        $resp->assertStatus(200);
+        $resp->assertJson(array_merge($details, [
+            'id' => $chapter->id, 'slug' => $chapter->slug, 'book_id' => $chapter->book_id
+        ]));
+        $this->assertActivityExists('chapter_update', $chapter);
+    }
+
+    public function test_delete_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+        $resp = $this->deleteJson($this->baseEndpoint . "/{$chapter->id}");
+
+        $resp->assertStatus(204);
+        $this->assertActivityExists('chapter_delete');
+    }
+
+    public function test_export_html_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/html");
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.html"');
+    }
+
+    public function test_export_plain_text_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/plaintext");
+        $resp->assertStatus(200);
+        $resp->assertSee($chapter->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.txt"');
+    }
+
+    public function test_export_pdf_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/pdf");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
+    }
+}
\ No newline at end of file
index 623fa6969a7128bf9652f9e50e16fa710d29ac54..1ad4d14b64e4c7137134552848b1f7c800dbed6f 100644 (file)
@@ -23,6 +23,16 @@ trait TestsApi
         return ["error" => ["code" => $code, "message" => $message]];
     }
 
+    /**
+     * Format the given (field_name => ["messages"]) array
+     * into a standard validation response format.
+     */
+    protected function validationResponse(array $messages): array
+    {
+        $err = $this->errorResponse("The given data was invalid.", 422);
+        $err['error']['validation'] = $messages;
+        return $err;
+    }
     /**
      * Get an approved API auth header.
      */
diff --git a/tests/AuditLogTest.php b/tests/AuditLogTest.php
new file mode 100644 (file)
index 0000000..a2cdc33
--- /dev/null
@@ -0,0 +1,109 @@
+<?php namespace Tests;
+
+use BookStack\Actions\Activity;
+use BookStack\Actions\ActivityService;
+use BookStack\Auth\UserRepo;
+use BookStack\Entities\Page;
+use BookStack\Entities\Repos\PageRepo;
+use Carbon\Carbon;
+
+class AuditLogTest extends TestCase
+{
+
+    public function test_only_accessible_with_right_permissions()
+    {
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+
+        $resp = $this->get('/settings/audit');
+        $this->assertPermissionError($resp);
+
+        $this->giveUserPermissions($viewer, ['settings-manage']);
+        $resp = $this->get('/settings/audit');
+        $this->assertPermissionError($resp);
+
+        $this->giveUserPermissions($viewer, ['users-manage']);
+        $resp = $this->get('/settings/audit');
+        $resp->assertStatus(200);
+        $resp->assertSeeText('Audit Log');
+    }
+
+    public function test_shows_activity()
+    {
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+        $page = Page::query()->first();
+        app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+        $activity = Activity::query()->orderBy('id', 'desc')->first();
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText($page->name);
+        $resp->assertSeeText('page_create');
+        $resp->assertSeeText($activity->created_at->toDateTimeString());
+        $resp->assertElementContains('.audit-log-user', $admin->name);
+    }
+
+    public function test_shows_name_for_deleted_items()
+    {
+        $this->actingAs( $this->getAdmin());
+        $page = Page::query()->first();
+        $pageName = $page->name;
+        app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+
+        app(PageRepo::class)->destroy($page);
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText('Deleted Item');
+        $resp->assertSeeText('Name: ' . $pageName);
+    }
+
+    public function test_shows_activity_for_deleted_users()
+    {
+        $viewer = $this->getViewer();
+        $this->actingAs($viewer);
+        $page = Page::query()->first();
+        app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+
+        $this->actingAs($this->getAdmin());
+        app(UserRepo::class)->destroy($viewer);
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText("[ID: {$viewer->id}] Deleted User");
+    }
+
+    public function test_filters_by_key()
+    {
+        $this->actingAs($this->getAdmin());
+        $page = Page::query()->first();
+        app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+
+        $resp = $this->get('settings/audit');
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?event=page_delete');
+        $resp->assertDontSeeText($page->name);
+    }
+
+    public function test_date_filters()
+    {
+        $this->actingAs($this->getAdmin());
+        $page = Page::query()->first();
+        app(ActivityService::class)->add($page, 'page_create', $page->book->id);
+
+        $yesterday = (Carbon::now()->subDay()->format('Y-m-d'));
+        $tomorrow = (Carbon::now()->addDay()->format('Y-m-d'));
+
+        $resp = $this->get('settings/audit?date_from=' . $yesterday);
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_from=' . $tomorrow);
+        $resp->assertDontSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_to=' . $tomorrow);
+        $resp->assertSeeText($page->name);
+
+        $resp = $this->get('settings/audit?date_to=' . $yesterday);
+        $resp->assertDontSeeText($page->name);
+    }
+
+}
\ No newline at end of file
index f1f47696641ac4978f2dc33c87a93b5b8805a3e6..e2b1e0cd66edcbae814bec9f055da290c8a0375d 100644 (file)
@@ -170,6 +170,11 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/register/confirm')
             ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
 
+        $this->visit('/')
+            ->seePageIs('/register/confirm/awaiting');
+
+        auth()->logout();
+
         $this->visit('/')->seePageIs('/login')
             ->type($user->email, '#email')
             ->type($user->password, '#password')
@@ -202,6 +207,10 @@ class AuthTest extends BrowserKitTest
             ->seePageIs('/register/confirm')
             ->seeInDatabase('users', ['name' => $user->name, 'email' => $user->email, 'email_confirmed' => false]);
 
+        $this->visit('/')
+            ->seePageIs('/register/confirm/awaiting');
+
+        auth()->logout();
         $this->visit('/')->seePageIs('/login')
             ->type($user->email, '#email')
             ->type($user->password, '#password')
@@ -213,13 +222,14 @@ class AuthTest extends BrowserKitTest
     public function test_user_creation()
     {
         $user = factory(User::class)->make();
+        $adminRole = Role::getRole('admin');
 
         $this->asAdmin()
             ->visit('/settings/users')
             ->click('Add New User')
             ->type($user->name, '#name')
             ->type($user->email, '#email')
-            ->check('roles[admin]')
+            ->check("roles[{$adminRole->id}]")
             ->type($user->password, '#password')
             ->type($user->password, '#password-confirm')
             ->press('Save')
@@ -381,6 +391,17 @@ class AuthTest extends BrowserKitTest
             ->seePageUrlIs($page->getUrl());
     }
 
+    public function test_login_intended_redirect_does_not_redirect_to_external_pages()
+    {
+        config()->set('app.url', 'http://localhost');
+        $this->setSettings(['app-public' => true]);
+
+        $this->get('/login', ['referer' => 'https://example.com']);
+        $login = $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
+
+        $login->assertRedirectedTo('http://localhost');
+    }
+
     public function test_login_authenticates_admins_on_all_guards()
     {
         $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
@@ -401,6 +422,18 @@ class AuthTest extends BrowserKitTest
         $this->assertFalse(auth('saml2')->check());
     }
 
+    public function test_failed_logins_are_logged_when_message_configured()
+    {
+        $log = $this->withTestLogger();
+        config()->set(['logging.failed_login.message' => 'Failed login for %u']);
+
+        $this->post('/login', ['email' => '[email protected]', 'password' => 'cattreedog']);
+        $this->assertTrue($log->hasWarningThatContains('Failed login for [email protected]'));
+
+        $this->post('/login', ['email' => '[email protected]', 'password' => 'password']);
+        $this->assertFalse($log->hasWarningThatContains('Failed login for [email protected]'));
+    }
+
     /**
      * Perform a login
      */
index ed8748f08ddff85a32304b61a5680d7b8cc36aa8..3cb39ca2c59c63a8315a76c44ebbaed1881bbfe2 100644 (file)
@@ -237,9 +237,9 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_maps_roles_and_retains_existing_roles()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
-        $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
+        $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
         $this->mockUser->attachRole($existingRole);
 
@@ -283,8 +283,8 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_maps_roles_and_removes_old_roles_if_set()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $existingRole = factory(Role::class)->create(['name' => 'ldaptester-existing']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $existingRole = factory(Role::class)->create(['display_name' => 'ldaptester-existing']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
         $this->mockUser->attachRole($existingRole);
 
@@ -323,15 +323,15 @@ class LdapTest extends BrowserKitTest
 
     public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
     {
-        $role = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
+        $role = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'ex-auth-a, test-second-param']);
         $this->asAdmin()->visit('/settings/roles/' . $role->id)
             ->see('ex-auth-a');
     }
 
     public function test_login_maps_roles_using_external_auth_ids_if_set()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
-        $roleToNotReceive = factory(Role::class)->create(['name' => 'ldaptester-not-receive', 'display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'ldaptester', 'external_auth_id' => 'test-second-param, ex-auth-a']);
+        $roleToNotReceive = factory(Role::class)->create(['display_name' => 'ex-auth-a', 'external_auth_id' => 'test-second-param']);
 
         app('config')->set([
             'services.ldap.user_to_groups' => true,
@@ -368,8 +368,8 @@ class LdapTest extends BrowserKitTest
 
     public function test_login_group_mapping_does_not_conflict_with_default_role()
     {
-        $roleToReceive = factory(Role::class)->create(['name' => 'ldaptester', 'display_name' => 'LdapTester']);
-        $roleToReceive2 = factory(Role::class)->create(['name' => 'ldaptester-second', 'display_name' => 'LdapTester Second']);
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $roleToReceive2 = factory(Role::class)->create(['display_name' => 'LdapTester Second']);
         $this->mockUser->forceFill(['external_auth_id' => $this->mockUser->name])->save();
 
         setting()->put('registration-role', $roleToReceive->id);
@@ -593,4 +593,59 @@ class LdapTest extends BrowserKitTest
 
         $this->see('A user with the email [email protected] already exists but with different credentials');
     }
+
+    public function test_login_with_email_confirmation_required_maps_groups_but_shows_confirmation_screen()
+    {
+        $roleToReceive = factory(Role::class)->create(['display_name' => 'LdapTester']);
+        $user = factory(User::class)->make();
+        setting()->put('registration-confirmation', 'true');
+
+        app('config')->set([
+            'services.ldap.user_to_groups' => true,
+            'services.ldap.group_attribute' => 'memberOf',
+            'services.ldap.remove_from_groups' => true,
+        ]);
+
+        $this->commonLdapMocks(1, 1, 3, 4, 3, 2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')
+            ->times(3)
+            ->andReturn(['count' => 1, 0 => [
+                'uid' => [$user->name],
+                'cn' => [$user->name],
+                'dn' => ['dc=test' . config('services.ldap.base_dn')],
+                'mail' => [$user->email],
+                'memberof' => [
+                    'count' => 1,
+                    0 => "cn=ldaptester,ou=groups,dc=example,dc=com",
+                ]
+            ]]);
+
+        $this->mockUserLogin()->seePageIs('/register/confirm');
+        $this->seeInDatabase('users', [
+            'email' => $user->email,
+            'email_confirmed' => false,
+        ]);
+
+        $user  = User::query()->where('email', '=', $user->email)->first();
+        $this->seeInDatabase('role_user', [
+            'user_id' => $user->id,
+            'role_id' => $roleToReceive->id
+        ]);
+
+        $homePage = $this->get('/');
+        $homePage->assertRedirectedTo('/register/confirm/awaiting');
+    }
+
+    public function test_failed_logins_are_logged_when_message_configured()
+    {
+        $log = $this->withTestLogger();
+        config()->set(['logging.failed_login.message' => 'Failed login for %u']);
+
+        $this->commonLdapMocks(1, 1, 1, 1, 1);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->andReturn(['count' => 0]);
+
+        $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
+        $this->assertTrue($log->hasWarningThatContains('Failed login for timmyjenkins'));
+    }
 }
index d0da4529735adcb7e2dfe0229c768cac4552aa2e..7303d4bd889fb9a13493eeda76101dbe0440f7c1 100644 (file)
@@ -290,6 +290,35 @@ class Saml2Test extends TestCase
         });
     }
 
+    public function test_group_sync_functions_when_email_confirmation_required()
+    {
+        setting()->put('registration-confirmation', 'true');
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => false,
+        ]);
+
+        $memberRole = factory(Role::class)->create(['external_auth_id' => 'member']);
+        $adminRole = Role::getSystemRole('admin');
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () use ($memberRole, $adminRole) {
+            $acsPost = $this->followingRedirects()->post('/saml2/acs');
+
+            $this->assertEquals('http://localhost/register/confirm', url()->current());
+            $acsPost->assertSee('Please check your email and click the confirmation button to access BookStack.');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $userRoleIds = $user->roles()->pluck('id');
+            $this->assertContains($memberRole->id, $userRoleIds, 'User was assigned to member role');
+            $this->assertContains($adminRole->id, $userRoleIds, 'User was assigned to admin role');
+            $this->assertTrue($user->email_confirmed == false, 'User email remains unconfirmed');
+        });
+
+        $homeGet = $this->get('/');
+        $homeGet->assertRedirect('/register/confirm/awaiting');
+    }
+
     protected function withGet(array $options, callable $callback)
     {
         return $this->withGlobal($_GET, $options, $callback);
index 2562f7e7de9e7aabfe5add97647b1dcee117064e..2198b2dd2c72decb348bd6421d8f664b52c3e793 100644 (file)
@@ -13,7 +13,7 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make(['parent_id' => 2]);
-        $resp = $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $resp = $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $resp->assertStatus(200);
         $resp->assertSee($comment->text);
@@ -36,11 +36,11 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make();
-        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $comment = $page->comments()->first();
         $newText = 'updated text content';
-        $resp = $this->putJson("/ajax/comment/$comment->id", [
+        $resp = $this->putJson("/comment/$comment->id", [
             'text' => $newText,
         ]);
 
@@ -60,11 +60,11 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $comment = factory(Comment::class)->make();
-        $this->postJson("/ajax/page/$page->id/comment", $comment->getAttributes());
+        $this->postJson("/comment/$page->id", $comment->getAttributes());
 
         $comment = $page->comments()->first();
 
-        $resp = $this->delete("/ajax/comment/$comment->id");
+        $resp = $this->delete("/comment/$comment->id");
         $resp->assertStatus(200);
 
         $this->assertDatabaseMissing('comments', [
@@ -75,7 +75,7 @@ class CommentTest extends TestCase
     public function test_comments_converts_markdown_input_to_html()
     {
         $page = Page::first();
-        $this->asAdmin()->postJson("/ajax/page/$page->id/comment", [
+        $this->asAdmin()->postJson("/comment/$page->id", [
             'text' => '# My Title',
         ]);
 
@@ -96,7 +96,7 @@ class CommentTest extends TestCase
         $page = Page::first();
 
         $script = '<script>const a = "script";</script>\n\n# sometextinthecomment';
-        $this->postJson("/ajax/page/$page->id/comment", [
+        $this->postJson("/comment/$page->id", [
             'text' => $script,
         ]);
 
@@ -105,7 +105,7 @@ class CommentTest extends TestCase
         $pageView->assertSee('sometextinthecomment');
 
         $comment = $page->comments()->first();
-        $this->putJson("/ajax/comment/$comment->id", [
+        $this->putJson("/comment/$comment->id", [
             'text' => $script . 'updated',
         ]);
 
index 72eb808dc4c581a84e03d85c74366ee79d3b238a..956e46c3713d4785cd7b8dcd11589cc6988dcfb0 100644 (file)
@@ -277,4 +277,20 @@ class EntitySearchTest extends TestCase
             $search->assertSee($expectedShelf->name);
         }
     }
+
+    public function test_search_works_on_updated_page_content()
+    {
+        $page = Page::query()->first();
+        $this->asEditor();
+
+        $update = $this->put($page->getUrl(), [
+            'name' => $page->name,
+            'html' => '<p>dog pandabearmonster spaghetti</p>',
+        ]);
+
+        $search = $this->asEditor()->get('/search?term=pandabearmonster');
+        $search->assertStatus(200);
+        $search->assertSeeText($page->name);
+        $search->assertSee($page->getUrl());
+    }
 }
index d7e4ec61c741f8b0e0822ba1307d7130ce8923c8..de1e025ade6a1e832cad59834f0c0774abf8ad1e 100644 (file)
@@ -273,15 +273,20 @@ class EntityTest extends BrowserKitTest
             ->seeInElement('#recently-updated-pages', $page->name);
     }
 
-    public function test_slug_multi_byte_lower_casing()
+    public function test_slug_multi_byte_url_safe()
     {
         $book = $this->newBook([
-            'name' => 'Ð\9aÐ\9dÐ\98Ð\93Ð\90'
+            'name' => 'инÑ\84оÑ\80маÑ\86иÑ\8f'
         ]);
 
-        $this->assertEquals('книга', $book->slug);
-    }
+        $this->assertEquals('informatsiya', $book->slug);
 
+        $book = $this->newBook([
+            'name' => '¿Qué?'
+        ]);
+
+        $this->assertEquals('que', $book->slug);
+    }
 
     public function test_slug_format()
     {
index d714c3229db7a8dfeecd587c67b0d9f41cde4457..d3b6224c4192b653586c55644154c0d4f62f8074 100644 (file)
@@ -304,4 +304,18 @@ class PageContentTest extends TestCase
             'level' => 3,
         ], $navMap[2]);
     }
+
+    public function test_page_text_decodes_html_entities()
+    {
+        $page = Page::query()->first();
+
+        $this->actingAs($this->getAdmin())
+            ->put($page->getUrl(''), [
+                'name' => 'Testing',
+                'html' => '<p>&quot;Hello &amp; welcome&quot;</p>',
+            ]);
+
+        $page->refresh();
+        $this->assertEquals('"Hello & welcome"', $page->text);
+    }
 }
index 5c984940d32569ce3e0b5d1cdce60bef2bf9d1cb..a0cf9e5fca9267aa6a8866f00f9e940e87b27ece 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Entity;
 
+use BookStack\Entities\Page;
 use BookStack\Entities\Repos\PageRepo;
 use Tests\BrowserKitTest;
 
@@ -101,4 +102,15 @@ class PageDraftTest extends BrowserKitTest
             ->dontSeeInElement('.book-contents', 'New Page');
     }
 
+    public function test_page_html_in_ajax_fetch_response()
+    {
+        $this->asAdmin();
+        $page = Page::query()->first();
+
+        $this->getJson('/ajax/page/' . $page->id);
+        $this->seeJson([
+            'html' => $page->html,
+        ]);
+    }
+
 }
index f8baccc5493f84e7ac0ce388bc81d837a8cdacf0..1e9dbd626b78fd011184e3549cbf239578365b6a 100644 (file)
@@ -24,6 +24,21 @@ class PageRevisionTest extends TestCase
         $revisionView->assertSee('new content');
     }
 
+    public function test_page_revision_preview_shows_content_of_revision()
+    {
+        $this->asEditor();
+
+        $pageRepo = app(PageRepo::class);
+        $page = Page::first();
+        $pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>new revision content</p>', 'summary' => 'page revision testing']);
+        $pageRevision = $page->revisions->last();
+        $pageRepo->update($page, ['name' => 'updated page', 'html' => '<p>Updated content</p>', 'summary' => 'page revision testing 2']);
+
+        $revisionView = $this->get($page->getUrl() . '/revisions/' . $pageRevision->id);
+        $revisionView->assertStatus(200);
+        $revisionView->assertSee('new revision content');
+    }
+
     public function test_page_revision_restore_updates_content()
     {
         $this->asEditor();
diff --git a/tests/Entity/SearchOptionsTest.php b/tests/Entity/SearchOptionsTest.php
new file mode 100644 (file)
index 0000000..727db55
--- /dev/null
@@ -0,0 +1,43 @@
+<?php namespace Tests\Entity;
+
+use BookStack\Entities\SearchOptions;
+use Tests\TestCase;
+
+class SearchOptionsTest extends TestCase
+{
+    public function test_from_string_parses_a_search_string_properly()
+    {
+        $options = SearchOptions::fromString('cat "dog" [tag=good] {is_tree}');
+
+        $this->assertEquals(['cat'], $options->searches);
+        $this->assertEquals(['dog'], $options->exacts);
+        $this->assertEquals(['tag=good'], $options->tags);
+        $this->assertEquals(['is_tree' => ''], $options->filters);
+    }
+
+    public function test_to_string_includes_all_items_in_the_correct_format()
+    {
+        $expected = 'cat "dog" [tag=good] {is_tree}';
+        $options = new SearchOptions;
+        $options->searches = ['cat'];
+        $options->exacts = ['dog'];
+        $options->tags = ['tag=good'];
+        $options->filters = ['is_tree' => ''];
+
+        $output = $options->toString();
+        foreach (explode(' ', $expected) as $term) {
+            $this->assertStringContainsString($term, $output);
+        }
+    }
+
+    public function test_correct_filter_values_are_set_from_string()
+    {
+        $opts = SearchOptions::fromString('{is_tree} {name:dan} {cat:happy}');
+
+        $this->assertEquals([
+            'is_tree' => '',
+            'name' => 'dan',
+            'cat' => 'happy',
+        ], $opts->filters);
+    }
+}
index 87d57ea0bf547b8cf76bc6ca2c3a8bfd45f341c2..e8a99cf781b6bd972ee43708025da51d081c1c51 100644 (file)
@@ -30,48 +30,6 @@ class TagTest extends BrowserKitTest
         return $entity;
     }
 
-    public function test_get_page_tags()
-    {
-        $page = $this->getEntityWithTags(Page::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/page/" . $page->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
-    public function test_get_chapter_tags()
-    {
-        $chapter = $this->getEntityWithTags(Chapter::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/chapter/" . $chapter->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
-    public function test_get_book_tags()
-    {
-        $book = $this->getEntityWithTags(Book::class);
-
-        // Add some other tags to check they don't interfere
-        factory(Tag::class, $this->defaultTagCount)->create();
-
-        $this->asAdmin()->get("/ajax/tags/get/book/" . $book->id)
-            ->shouldReturnJson();
-
-        $json = json_decode($this->response->getContent());
-        $this->assertTrue(count($json) === $this->defaultTagCount, "Returned JSON item count is not as expected");
-    }
-
     public function test_tag_name_suggestions()
     {
         // Create some tags with similar names to test with
index a5e4a4a5e58b9d1e237fa37240cca6d7079154de..8f6867cdeb0c357e16f9b7ce7df7df3813984200 100644 (file)
@@ -1,5 +1,8 @@
 <?php namespace Tests;
 
+use BookStack\Entities\Book;
+use Illuminate\Support\Facades\Log;
+
 class ErrorTest extends TestCase
 {
 
@@ -18,4 +21,21 @@ class ErrorTest extends TestCase
         $notFound->assertDontSeeText('Log in');
         $notFound->assertSeeText('tester');
     }
+
+    public function test_item_not_found_does_not_get_logged_to_file()
+    {
+        $this->actingAs($this->getViewer());
+        $handler = $this->withTestLogger();
+        $book = Book::query()->first();
+
+        // Ensure we're seeing errors
+        Log::error('cat');
+        $this->assertTrue($handler->hasErrorThatContains('cat'));
+
+        $this->get('/books/arandomnotfouindbook');
+        $this->get($book->getUrl('/chapter/arandomnotfouindchapter'));
+        $this->get($book->getUrl('/chapter/arandomnotfouindpages'));
+
+        $this->assertCount(1, $handler->getRecords());
+    }
 }
\ No newline at end of file
index 99080d354c4c239fae8261e8de53d06681b7e7fe..73060c834383726a138fa8ce180980123070104a 100644 (file)
@@ -2,10 +2,8 @@
 
 use BookStack\Entities\Bookshelf;
 use BookStack\Entities\Page;
-use BookStack\Auth\Permissions\PermissionsRepo;
 use BookStack\Auth\Role;
 use Laravel\BrowserKitTesting\HttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Tests\BrowserKitTest;
 
 class RolesTest extends BrowserKitTest
@@ -59,7 +57,7 @@ class RolesTest extends BrowserKitTest
             ->type('Test Role', 'display_name')
             ->type('A little test description', 'description')
             ->press('Save Role')
-            ->seeInDatabase('roles', ['display_name' => $testRoleName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seeInDatabase('roles', ['display_name' => $testRoleName, 'description' => $testRoleDesc])
             ->seePageIs('/settings/roles');
         // Updating
         $this->asAdmin()->visit('/settings/roles')
@@ -67,7 +65,7 @@ class RolesTest extends BrowserKitTest
             ->click($testRoleName)
             ->type($testRoleUpdateName, '#display_name')
             ->press('Save Role')
-            ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'name' => 'test-role', 'description' => $testRoleDesc])
+            ->seeInDatabase('roles', ['display_name' => $testRoleUpdateName, 'description' => $testRoleDesc])
             ->seePageIs('/settings/roles');
         // Deleting
         $this->asAdmin()->visit('/settings/roles')
@@ -101,6 +99,25 @@ class RolesTest extends BrowserKitTest
         $this->see('This user is the only user assigned to the administrator role');
     }
 
+    public function test_migrate_users_on_delete_works()
+    {
+        $roleA = Role::query()->create(['display_name' => 'Delete Test A']);
+        $roleB = Role::query()->create(['display_name' => 'Delete Test B']);
+        $this->user->attachRole($roleB);
+
+        $this->assertCount(0, $roleA->users()->get());
+        $this->assertCount(1, $roleB->users()->get());
+
+        $deletePage = $this->asAdmin()->get("/settings/roles/delete/{$roleB->id}");
+        $deletePage->seeElement('select[name=migrate_role_id]');
+        $this->asAdmin()->delete("/settings/roles/delete/{$roleB->id}", [
+            'migrate_role_id' => $roleA->id,
+        ]);
+
+        $this->assertCount(1, $roleA->users()->get());
+        $this->assertEquals($this->user->id, $roleA->users()->first()->id);
+    }
+
     public function test_manage_user_permission()
     {
         $this->actingAs($this->user)->visit('/settings/users')
@@ -669,9 +686,11 @@ class RolesTest extends BrowserKitTest
     public function test_public_role_visible_in_user_edit_screen()
     {
         $user = \BookStack\Auth\User::first();
+        $adminRole = Role::getSystemRole('admin');
+        $publicRole = Role::getSystemRole('public');
         $this->asAdmin()->visit('/settings/users/' . $user->id)
-            ->seeElement('[name="roles[admin]"]')
-            ->seeElement('[name="roles[public]"]');
+            ->seeElement('[name="roles['.$adminRole->id.']"]')
+            ->seeElement('[name="roles['.$publicRole->id.']"]');
     }
 
     public function test_public_role_visible_in_role_listing()
@@ -684,9 +703,8 @@ class RolesTest extends BrowserKitTest
     public function test_public_role_visible_in_default_role_setting()
     {
         $this->asAdmin()->visit('/settings')
-            ->seeElement('[data-role-name="admin"]')
-            ->seeElement('[data-role-name="public"]');
-
+            ->seeElement('[data-system-role-name="admin"]')
+            ->seeElement('[data-system-role-name="public"]');
     }
 
     public function test_public_role_not_deleteable()
@@ -852,7 +870,7 @@ class RolesTest extends BrowserKitTest
 
     private function addComment($page) {
         $comment = factory(\BookStack\Actions\Comment::class)->make();
-        $url = "/ajax/page/$page->id/comment";
+        $url = "/comment/$page->id";
         $request = [
             'text' => $comment->text,
             'html' => $comment->html
@@ -865,7 +883,7 @@ class RolesTest extends BrowserKitTest
 
     private function updateComment($commentId) {
         $comment = factory(\BookStack\Actions\Comment::class)->make();
-        $url = "/ajax/comment/$commentId";
+        $url = "/comment/$commentId";
         $request = [
             'text' => $comment->text,
             'html' => $comment->html
@@ -875,7 +893,7 @@ class RolesTest extends BrowserKitTest
     }
 
     private function deleteComment($commentId) {
-         $url = '/ajax/comment/' . $commentId;
+         $url = '/comment/' . $commentId;
          return $this->json('DELETE', $url);
     }
 
index f7b7d5edf3ef23e4b3d59a0189f4707faa00f1dd..c7659a02dabae0168348553d4f0efd360f5598d9 100644 (file)
@@ -16,7 +16,10 @@ use BookStack\Entities\Repos\PageRepo;
 use BookStack\Settings\SettingService;
 use BookStack\Uploads\HttpFetcher;
 use Illuminate\Support\Env;
+use Illuminate\Support\Facades\Log;
 use Mockery;
+use Monolog\Handler\TestHandler;
+use Monolog\Logger;
 use Throwable;
 
 trait SharedTestHelpers
@@ -69,14 +72,14 @@ trait SharedTestHelpers
     }
 
     /**
-     * Get an instance of a user with 'viewer' permissions
-     * @param $attributes
-     * @return mixed
+     * Get an instance of a user with 'viewer' permissions.
      */
-    protected function getViewer($attributes = [])
+    protected function getViewer(array $attributes = []): User
     {
         $user = Role::getRole('viewer')->users()->first();
-        if (!empty($attributes)) $user->forceFill($attributes)->save();
+        if (!empty($attributes)) {
+            $user->forceFill($attributes)->save();
+        }
         return $user;
     }
 
@@ -277,4 +280,22 @@ trait SharedTestHelpers
         $this->assertStringStartsWith('You do not have permission to access', $error);
     }
 
+    /**
+     * Set a test handler as the logging interface for the application.
+     * Allows capture of logs for checking against during tests.
+     */
+    protected function withTestLogger(): TestHandler
+    {
+        $monolog = new Logger('testing');
+        $testHandler = new TestHandler();
+        $monolog->pushHandler($testHandler);
+
+        Log::extend('testing', function() use ($monolog) {
+            return $monolog;
+        });
+        Log::setDefaultDriver('testing');
+
+        return $testHandler;
+    }
+
 }
\ No newline at end of file
index 69b737d7df4ac3a88b03b0fcbffa07467d516a6b..1374b3aa9e288b3405c61fe2afe5834d3f40c481 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Unit;
 
+use Illuminate\Support\Facades\Log;
 use Tests\TestCase;
 
 /**
@@ -36,6 +37,28 @@ class ConfigTest extends TestCase
         $this->checkEnvConfigResult('APP_URL', $oldDefault, 'app.url', '');
     }
 
+    public function test_errorlog_plain_webserver_channel()
+    {
+        // We can't full test this due to it being targeted for the SAPI logging handler
+        // so we just overwrite that component so we can capture the error log output.
+        config()->set([
+            'logging.channels.errorlog_plain_webserver.handler_with' => [0],
+        ]);
+
+        $temp = tempnam(sys_get_temp_dir(), 'bs-test');
+        $original = ini_set( 'error_log', $temp);
+
+        Log::channel('errorlog_plain_webserver')->info('Aww, look, a cute puppy');
+
+        ini_set( 'error_log', $original);
+
+        $output = file_get_contents($temp);
+        $this->assertStringContainsString('Aww, look, a cute puppy', $output);
+        $this->assertStringNotContainsString('INFO', $output);
+        $this->assertStringNotContainsString('info', $output);
+        $this->assertStringNotContainsString('testing', $output);
+    }
+
     /**
      * Set an environment variable of the given name and value
      * then check the given config key to see if it matches the given result.
index feff4797718baa4904dce7d80dcbaf4aca5180a6..b9f485da13f0743178049d2e314db04fccd18536 100644 (file)
@@ -5,17 +5,6 @@ use Tests\TestCase;
 class UrlTest extends TestCase
 {
 
-    public function test_request_url_takes_custom_url_into_account()
-    {
-        config()->set('app.url', 'http://example.com/bookstack');
-        $this->get('/');
-        $this->assertEquals('http://example.com/bookstack', request()->getUri());
-
-        config()->set('app.url', 'http://example.com/docs/content');
-        $this->get('/');
-        $this->assertEquals('http://example.com/docs/content', request()->getUri());
-    }
-
     public function test_url_helper_takes_custom_url_into_account()
     {
         $this->runWithEnv('APP_URL', 'http://example.com/bookstack', function() {
index e98a90b35d2034aaa701a726a42fcb5de4939b30..a7efe08abb981d24d55e2a528d3cf2017c942033 100644 (file)
@@ -36,7 +36,7 @@ class AttachmentTest extends TestCase
     protected function deleteUploads()
     {
         $fileService = $this->app->make(\BookStack\Uploads\AttachmentService::class);
-        foreach (\BookStack\Uploads\Attachment::all() as $file) {
+        foreach (Attachment::all() as $file) {
             $fileService->deleteFile($file);
         }
     }
@@ -110,12 +110,12 @@ class AttachmentTest extends TestCase
         $this->asAdmin();
 
         $linkReq = $this->call('POST', 'attachments/link', [
-            'link' => 'https://example.com',
-            'name' => 'Example Attachment Link',
-            'uploaded_to' => $page->id,
+            'attachment_link_url' => 'https://example.com',
+            'attachment_link_name' => 'Example Attachment Link',
+            'attachment_link_uploaded_to' => $page->id,
         ]);
 
-        $expectedResp = [
+        $expectedData = [
             'path' => 'https://example.com',
             'name' => 'Example Attachment Link',
             'uploaded_to' => $page->id,
@@ -127,8 +127,7 @@ class AttachmentTest extends TestCase
         ];
 
         $linkReq->assertStatus(200);
-        $linkReq->assertJson($expectedResp);
-        $this->assertDatabaseHas('attachments', $expectedResp);
+        $this->assertDatabaseHas('attachments', $expectedData);
         $attachment = Attachment::orderBy('id', 'desc')->take(1)->first();
 
         $pageGet = $this->get($page->getUrl());
@@ -147,28 +146,27 @@ class AttachmentTest extends TestCase
         $this->asAdmin();
 
         $this->call('POST', 'attachments/link', [
-            'link' => 'https://example.com',
-            'name' => 'Example Attachment Link',
-            'uploaded_to' => $page->id,
+            'attachment_link_url' => 'https://example.com',
+            'attachment_link_name' => 'Example Attachment Link',
+            'attachment_link_uploaded_to' => $page->id,
         ]);
 
-        $attachmentId = \BookStack\Uploads\Attachment::first()->id;
+        $attachmentId = Attachment::first()->id;
 
         $update = $this->call('PUT', 'attachments/' . $attachmentId, [
-            'uploaded_to' => $page->id,
-            'name' => 'My new attachment name',
-            'link' => 'https://test.example.com'
+            'attachment_edit_name' => 'My new attachment name',
+            'attachment_edit_url' => 'https://test.example.com'
         ]);
 
-        $expectedResp = [
+        $expectedData = [
+            'id' => $attachmentId,
             'path' => 'https://test.example.com',
             'name' => 'My new attachment name',
             'uploaded_to' => $page->id
         ];
 
         $update->assertStatus(200);
-        $update->assertJson($expectedResp);
-        $this->assertDatabaseHas('attachments', $expectedResp);
+        $this->assertDatabaseHas('attachments', $expectedData);
 
         $this->deleteUploads();
     }
@@ -184,7 +182,7 @@ class AttachmentTest extends TestCase
         $filePath = storage_path($attachment->path);
         $this->assertTrue(file_exists($filePath), 'File at path ' . $filePath . ' does not exist');
 
-        $attachment = \BookStack\Uploads\Attachment::first();
+        $attachment = Attachment::first();
         $this->delete($attachment->getUrl());
 
         $this->assertDatabaseMissing('attachments', [
index 416927ac93170bb1649429d7fdd354932befb6af..08ac633268c68fd71241069ce24ebb137104c9da 100644 (file)
@@ -71,11 +71,7 @@ class ImageTest extends TestCase
         $newName = Str::random();
         $update = $this->put('/images/' . $image->id, ['name' => $newName]);
         $update->assertSuccessful();
-        $update->assertJson([
-            'id' => $image->id,
-            'name' => $newName,
-            'type' => 'gallery',
-        ]);
+        $update->assertSee($newName);
 
         $this->deleteImage($imgDetails['path']);
 
@@ -92,31 +88,22 @@ class ImageTest extends TestCase
         $imgDetails = $this->uploadGalleryImage();
         $image = Image::query()->first();
 
-        $emptyJson = ['images' => [], 'has_more' => false];
-        $resultJson = [
-            'images' => [
-                [
-                    'id' => $image->id,
-                    'name' => $imgDetails['name'],
-                ]
-            ],
-            'has_more' => false,
-        ];
-
         $pageId = $imgDetails['page']->id;
         $firstPageRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}");
-        $firstPageRequest->assertSuccessful()->assertJson($resultJson);
+        $firstPageRequest->assertSuccessful()->assertElementExists('div');
+        $firstPageRequest->assertSuccessful()->assertSeeText($image->name);
 
         $secondPageRequest = $this->get("/images/gallery?page=2&uploaded_to={$pageId}");
-        $secondPageRequest->assertSuccessful()->assertExactJson($emptyJson);
+        $secondPageRequest->assertSuccessful()->assertElementNotExists('div');
 
         $namePartial = substr($imgDetails['name'], 0, 3);
         $searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
-        $searchHitRequest->assertSuccessful()->assertJson($resultJson);
+        $searchHitRequest->assertSuccessful()->assertSee($imgDetails['name']);
 
         $namePartial = Str::random(16);
-        $searchHitRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
-        $searchHitRequest->assertSuccessful()->assertExactJson($emptyJson);
+        $searchFailRequest = $this->get("/images/gallery?page=1&uploaded_to={$pageId}&search={$namePartial}");
+        $searchFailRequest->assertSuccessful()->assertDontSee($imgDetails['name']);
+        $searchFailRequest->assertSuccessful()->assertElementNotExists('div');
     }
 
     public function test_image_usage()
@@ -131,14 +118,10 @@ class ImageTest extends TestCase
         $page->html = '<img src="'.$image->url.'">';
         $page->save();
 
-        $usage = $this->get('/images/usage/' . $image->id);
+        $usage = $this->get('/images/edit/' . $image->id . '?delete=true');
         $usage->assertSuccessful();
-        $usage->assertJson([
-            [
-                'id' => $page->id,
-                'name' => $page->name
-            ]
-        ]);
+        $usage->assertSeeText($page->name);
+        $usage->assertSee($page->getUrl());
 
         $this->deleteImage($imgDetails['path']);
     }
@@ -199,6 +182,38 @@ class ImageTest extends TestCase
         $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded double extension file was uploaded but should have been stopped');
     }
 
+    public function test_url_entities_removed_from_filenames()
+    {
+        $this->asEditor();
+        $badNames = [
+            "bad-char-#-image.png",
+            "bad-char-?-image.png",
+            "?#.png",
+            "?.png",
+            "#.png",
+        ];
+        foreach ($badNames as $name) {
+            $galleryFile = $this->getTestImage($name);
+            $page = Page::first();
+            $badPath = $this->getTestImagePath('gallery', $name);
+            $this->deleteImage($badPath);
+
+            $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
+            $upload->assertStatus(200);
+
+            $lastImage = Image::query()->latest('id')->first();
+            $newFileName = explode('.', basename($lastImage->path))[0];
+
+            $this->assertEquals($lastImage->name, $name);
+            $this->assertFalse(strpos($lastImage->path, $name), 'Path contains original image name');
+            $this->assertFalse(file_exists(public_path($badPath)), 'Uploaded image file name was not stripped of url entities');
+
+            $this->assertTrue(strlen($newFileName) > 0, 'File name was reduced to nothing');
+
+            $this->deleteImage($lastImage->path);
+        }
+    }
+
     public function test_secure_images_uploads_to_correct_place()
     {
         config()->set('filesystems.images', 'local_secure');
@@ -262,10 +277,11 @@ class ImageTest extends TestCase
         $page = Page::first();
         $this->asAdmin();
         $imageName = 'first-image.png';
+        $relPath = $this->getTestImagePath('gallery', $imageName);
+        $this->deleteImage($relPath);
 
         $this->uploadImage($imageName, $page->id);
         $image = Image::first();
-        $relPath = $this->getTestImagePath('gallery', $imageName);
 
         $delete = $this->delete( '/images/' . $image->id);
         $delete->assertStatus(200);
@@ -278,6 +294,31 @@ class ImageTest extends TestCase
         $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded image has not been deleted as expected');
     }
 
+    public function test_image_delete_does_not_delete_similar_images()
+    {
+        $page = Page::first();
+        $this->asAdmin();
+        $imageName = 'first-image.png';
+
+        $relPath = $this->getTestImagePath('gallery', $imageName);
+        $this->deleteImage($relPath);
+
+        $this->uploadImage($imageName, $page->id);
+        $this->uploadImage($imageName, $page->id);
+        $this->uploadImage($imageName, $page->id);
+
+        $image = Image::first();
+        $folder = public_path(dirname($relPath));
+        $imageCount = count(glob($folder . '/*'));
+
+        $delete = $this->delete( '/images/' . $image->id);
+        $delete->assertStatus(200);
+
+        $newCount = count(glob($folder . '/*'));
+        $this->assertEquals($imageCount - 1, $newCount, 'More files than expected have been deleted');
+        $this->assertFalse(file_exists(public_path($relPath)), 'Uploaded image has not been deleted as expected');
+    }
+
     protected function getTestProfileImage()
     {
         $imageName = 'profile.png';
index 251a61c9f5841c2ad9513fbfc7140594b3adb4fc..f5d1032ad1f7e6844f5f379a9db85438193ccc01 100644 (file)
@@ -39,11 +39,8 @@ trait UsesImages
 
     /**
      * Get the path for a test image.
-     * @param $type
-     * @param $fileName
-     * @return string
      */
-    protected function getTestImagePath($type, $fileName)
+    protected function getTestImagePath(string $type, string $fileName): string
     {
         return '/uploads/images/' . $type . '/' . Date('Y-m') . '/' . $fileName;
     }
diff --git a/version b/version
index e22b5a597f76bdb7b9ce8c40cab8045ccfeb4d5c..9388ecbd52f057369df5e2cdb26ebfbe7a6c95da 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-v0.29.3
+v0.30.0
diff --git a/webpack.config.js b/webpack.config.js
deleted file mode 100644 (file)
index e496340..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-const path = require('path');
-const dev = process.env.NODE_ENV !== 'production';
-
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-
-const config = {
-    target: 'web',
-    mode: dev? 'development' : 'production',
-    entry: {
-        app: './resources/js/index.js',
-        styles: './resources/sass/styles.scss',
-        "export-styles": './resources/sass/export-styles.scss',
-        "print-styles": './resources/sass/print-styles.scss',
-    },
-    output: {
-        filename: '[name].js',
-        path: path.resolve(__dirname, 'public/dist')
-    },
-    module: {
-        rules: [
-            {
-                test: /\.scss$/,
-                use: [
-                    {
-                        loader: MiniCssExtractPlugin.loader,
-                        options: {}
-                    },
-                    {
-                        loader: "css-loader", options: {
-                        sourceMap: dev
-                    }
-                    }, {
-                        loader: "sass-loader", options: {
-                            sourceMap: dev
-                        }
-                    }
-                ]
-            }
-        ]
-    },
-    plugins: [
-        new MiniCssExtractPlugin({
-            filename: "[name].css",
-        }),
-    ]
-};
-
-if (dev) {
-    config['devtool'] = 'inline-source-map';
-}
-
-module.exports = config;
\ No newline at end of file