]> BookStack Code Mirror - bookstack/commitdiff
Merge branch 'master' of https://github.com/arjvand/BookStack into arjvand-master
authorDan Brown <redacted>
Sat, 26 Jun 2021 10:19:21 +0000 (11:19 +0100)
committerDan Brown <redacted>
Sat, 26 Jun 2021 10:19:21 +0000 (11:19 +0100)
380 files changed:
.env.example.complete
.github/translators.txt
app/Actions/Favourite.php [new file with mode: 0644]
app/Actions/Tag.php
app/Actions/View.php
app/Actions/ViewService.php [deleted file]
app/Auth/Access/Guards/LdapSessionGuard.php
app/Auth/Access/LdapService.php
app/Auth/Access/SocialAuthService.php
app/Auth/Permissions/PermissionService.php
app/Auth/Role.php
app/Auth/User.php
app/Auth/UserRepo.php
app/Config/app.php
app/Config/dompdf.php
app/Config/saml2.php
app/Config/services.php
app/Console/Commands/ClearViews.php
app/Entities/Models/Chapter.php
app/Entities/Models/Entity.php
app/Entities/Models/Page.php
app/Entities/Queries/EntityQuery.php [new file with mode: 0644]
app/Entities/Queries/Popular.php [new file with mode: 0644]
app/Entities/Queries/RecentlyViewed.php [new file with mode: 0644]
app/Entities/Queries/TopFavourites.php [new file with mode: 0644]
app/Entities/Repos/PageRepo.php
app/Entities/Tools/ExportFormatter.php
app/Entities/Tools/Markdown/CustomParagraphConverter.php [new file with mode: 0644]
app/Entities/Tools/Markdown/HtmlToMarkdown.php [new file with mode: 0644]
app/Entities/Tools/NextPreviousContentLocator.php [new file with mode: 0644]
app/Entities/Tools/PageContent.php
app/Entities/Tools/TrashCan.php
app/Exceptions/Handler.php
app/Exceptions/NotifyException.php
app/Exceptions/PrettyException.php
app/Facades/Images.php [deleted file]
app/Facades/Views.php [deleted file]
app/Http/Controllers/Api/BookExportApiController.php
app/Http/Controllers/Api/ChapterExportApiController.php
app/Http/Controllers/Api/PageApiController.php
app/Http/Controllers/Api/PageExportApiController.php
app/Http/Controllers/AttachmentController.php
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/BookController.php
app/Http/Controllers/BookExportController.php
app/Http/Controllers/BookshelfController.php
app/Http/Controllers/ChapterController.php
app/Http/Controllers/ChapterExportController.php
app/Http/Controllers/Controller.php
app/Http/Controllers/FavouriteController.php [new file with mode: 0644]
app/Http/Controllers/HomeController.php
app/Http/Controllers/Images/ImageController.php
app/Http/Controllers/PageController.php
app/Http/Controllers/PageExportController.php
app/Http/Controllers/SearchController.php
app/Http/Middleware/Localization.php
app/Interfaces/Favouritable.php [new file with mode: 0644]
app/Interfaces/Viewable.php [new file with mode: 0644]
app/Providers/CustomFacadeProvider.php
app/Theming/ThemeService.php
app/Uploads/Attachment.php
app/Uploads/AttachmentService.php
app/Uploads/Image.php
app/Uploads/ImageRepo.php
app/Uploads/ImageService.php
app/Uploads/UserAvatars.php
app/Util/HtmlContentFilter.php [new file with mode: 0644]
composer.json
composer.lock
database/migrations/2017_03_19_091553_create_search_index_table.php
database/migrations/2018_07_15_173514_add_role_external_auth_id.php
database/migrations/2018_08_04_115700_create_bookshelves_table.php
database/migrations/2021_03_08_215138_add_user_slug.php
database/migrations/2021_05_15_173110_create_favourites_table.php [new file with mode: 0644]
dev/docker/Dockerfile
dev/docs/logical-theme-system.md
package-lock.json
package.json
resources/icons/star-circle.svg
resources/icons/star-outline.svg [new file with mode: 0644]
resources/icons/tag.svg
resources/js/components/attachments-list.js [new file with mode: 0644]
resources/js/components/dropdown.js
resources/js/components/header-mobile-toggle.js
resources/js/components/index.js
resources/js/components/markdown-editor.js
resources/js/components/tri-layout.js
resources/js/components/wysiwyg-editor.js
resources/js/services/drawio.js
resources/js/services/http.js
resources/lang/ar/activities.php
resources/lang/ar/auth.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
resources/lang/bg/common.php
resources/lang/bg/components.php
resources/lang/bg/entities.php
resources/lang/bg/errors.php
resources/lang/bg/settings.php
resources/lang/bg/validation.php
resources/lang/bs/activities.php
resources/lang/bs/common.php
resources/lang/bs/entities.php
resources/lang/bs/errors.php
resources/lang/bs/settings.php
resources/lang/ca/activities.php
resources/lang/ca/auth.php
resources/lang/ca/common.php
resources/lang/ca/entities.php
resources/lang/ca/errors.php
resources/lang/ca/passwords.php
resources/lang/ca/settings.php
resources/lang/ca/validation.php
resources/lang/cs/activities.php
resources/lang/cs/common.php
resources/lang/cs/entities.php
resources/lang/cs/errors.php
resources/lang/cs/settings.php
resources/lang/da/activities.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/settings.php
resources/lang/da/validation.php
resources/lang/de/activities.php
resources/lang/de/common.php
resources/lang/de/entities.php
resources/lang/de/errors.php
resources/lang/de/settings.php
resources/lang/de_informal/activities.php
resources/lang/de_informal/common.php
resources/lang/de_informal/entities.php
resources/lang/de_informal/errors.php
resources/lang/de_informal/settings.php
resources/lang/en/activities.php
resources/lang/en/common.php
resources/lang/en/entities.php
resources/lang/en/errors.php
resources/lang/en/settings.php
resources/lang/es/activities.php
resources/lang/es/common.php
resources/lang/es/entities.php
resources/lang/es/errors.php
resources/lang/es/settings.php
resources/lang/es_AR/activities.php
resources/lang/es_AR/common.php
resources/lang/es_AR/entities.php
resources/lang/es_AR/errors.php
resources/lang/es_AR/settings.php
resources/lang/fa/activities.php
resources/lang/fa/common.php
resources/lang/fa/entities.php
resources/lang/fa/errors.php
resources/lang/fa/settings.php
resources/lang/fr/activities.php
resources/lang/fr/common.php
resources/lang/fr/entities.php
resources/lang/fr/errors.php
resources/lang/fr/settings.php
resources/lang/he/activities.php
resources/lang/he/common.php
resources/lang/he/entities.php
resources/lang/he/errors.php
resources/lang/he/settings.php
resources/lang/hr/activities.php [new file with mode: 0644]
resources/lang/hr/auth.php [new file with mode: 0644]
resources/lang/hr/common.php [new file with mode: 0644]
resources/lang/hr/components.php [new file with mode: 0644]
resources/lang/hr/entities.php [new file with mode: 0644]
resources/lang/hr/errors.php [new file with mode: 0644]
resources/lang/hr/pagination.php [new file with mode: 0644]
resources/lang/hr/passwords.php [new file with mode: 0644]
resources/lang/hr/settings.php [new file with mode: 0644]
resources/lang/hr/validation.php [new file with mode: 0644]
resources/lang/hu/activities.php
resources/lang/hu/common.php
resources/lang/hu/entities.php
resources/lang/hu/errors.php
resources/lang/hu/settings.php
resources/lang/id/activities.php
resources/lang/id/common.php
resources/lang/id/entities.php
resources/lang/id/errors.php
resources/lang/id/settings.php
resources/lang/id/validation.php
resources/lang/it/activities.php
resources/lang/it/common.php
resources/lang/it/components.php
resources/lang/it/entities.php
resources/lang/it/errors.php
resources/lang/it/passwords.php
resources/lang/it/settings.php
resources/lang/it/validation.php
resources/lang/ja/activities.php
resources/lang/ja/common.php
resources/lang/ja/entities.php
resources/lang/ja/errors.php
resources/lang/ja/settings.php
resources/lang/ko/activities.php
resources/lang/ko/common.php
resources/lang/ko/entities.php
resources/lang/ko/errors.php
resources/lang/ko/settings.php
resources/lang/lv/activities.php
resources/lang/lv/common.php
resources/lang/lv/entities.php
resources/lang/lv/errors.php
resources/lang/lv/settings.php
resources/lang/nb/activities.php
resources/lang/nb/common.php
resources/lang/nb/entities.php
resources/lang/nb/errors.php
resources/lang/nb/settings.php
resources/lang/nl/activities.php
resources/lang/nl/auth.php
resources/lang/nl/common.php
resources/lang/nl/components.php
resources/lang/nl/entities.php
resources/lang/nl/errors.php
resources/lang/nl/settings.php
resources/lang/nl/validation.php
resources/lang/pl/activities.php
resources/lang/pl/common.php
resources/lang/pl/entities.php
resources/lang/pl/errors.php
resources/lang/pl/settings.php
resources/lang/pt/activities.php
resources/lang/pt/common.php
resources/lang/pt/entities.php
resources/lang/pt/errors.php
resources/lang/pt/settings.php
resources/lang/pt_BR/activities.php
resources/lang/pt_BR/common.php
resources/lang/pt_BR/entities.php
resources/lang/pt_BR/errors.php
resources/lang/pt_BR/settings.php
resources/lang/ru/activities.php
resources/lang/ru/common.php
resources/lang/ru/entities.php
resources/lang/ru/errors.php
resources/lang/ru/settings.php
resources/lang/sk/activities.php
resources/lang/sk/common.php
resources/lang/sk/entities.php
resources/lang/sk/errors.php
resources/lang/sk/settings.php
resources/lang/sl/activities.php
resources/lang/sl/common.php
resources/lang/sl/entities.php
resources/lang/sl/errors.php
resources/lang/sl/settings.php
resources/lang/sv/activities.php
resources/lang/sv/common.php
resources/lang/sv/entities.php
resources/lang/sv/errors.php
resources/lang/sv/settings.php
resources/lang/tr/activities.php
resources/lang/tr/common.php
resources/lang/tr/entities.php
resources/lang/tr/errors.php
resources/lang/tr/settings.php
resources/lang/uk/activities.php
resources/lang/uk/common.php
resources/lang/uk/entities.php
resources/lang/uk/errors.php
resources/lang/uk/settings.php
resources/lang/vi/activities.php
resources/lang/vi/common.php
resources/lang/vi/entities.php
resources/lang/vi/errors.php
resources/lang/vi/settings.php
resources/lang/zh_CN/activities.php
resources/lang/zh_CN/auth.php
resources/lang/zh_CN/common.php
resources/lang/zh_CN/entities.php
resources/lang/zh_CN/errors.php
resources/lang/zh_CN/settings.php
resources/lang/zh_TW/activities.php
resources/lang/zh_TW/common.php
resources/lang/zh_TW/entities.php
resources/lang/zh_TW/errors.php
resources/lang/zh_TW/settings.php
resources/sass/_blocks.scss
resources/sass/_buttons.scss
resources/sass/_components.scss
resources/sass/_forms.scss
resources/sass/_header.scss
resources/sass/_layout.scss
resources/sass/_lists.scss
resources/sass/_tables.scss
resources/sass/_text.scss
resources/sass/export-styles.scss
resources/sass/print-styles.scss
resources/sass/styles.scss
resources/views/api-docs/index.blade.php
resources/views/attachments/list.blade.php
resources/views/attachments/manager-list.blade.php
resources/views/auth/forms/login/saml2.blade.php
resources/views/base.blade.php
resources/views/books/export.blade.php
resources/views/books/form.blade.php
resources/views/books/show.blade.php
resources/views/chapters/export.blade.php
resources/views/chapters/form.blade.php
resources/views/chapters/show.blade.php
resources/views/common/detailed-listing-paginated.blade.php [moved from resources/views/pages/detailed-listing.blade.php with 82% similarity]
resources/views/common/detailed-listing-with-more.blade.php [new file with mode: 0644]
resources/views/common/footer.blade.php
resources/views/common/header.blade.php
resources/views/common/home-book.blade.php
resources/views/common/home-custom.blade.php
resources/views/common/home-shelves.blade.php
resources/views/common/home-sidebar.blade.php
resources/views/common/home.blade.php
resources/views/common/parts/skip-to-content.blade.php [new file with mode: 0644]
resources/views/components/expand-toggle.blade.php
resources/views/components/image-manager-form.blade.php
resources/views/components/page-picker.blade.php
resources/views/components/tag-list.blade.php
resources/views/errors/404.blade.php
resources/views/errors/500.blade.php
resources/views/errors/parts/not-found-text.blade.php [new file with mode: 0644]
resources/views/export-layout.blade.php [new file with mode: 0644]
resources/views/form/entity-permissions.blade.php
resources/views/pages/edit.blade.php
resources/views/pages/export.blade.php
resources/views/pages/markdown-editor.blade.php
resources/views/pages/revisions.blade.php
resources/views/pages/show.blade.php
resources/views/pages/wysiwyg-editor.blade.php
resources/views/partials/custom-head-content.blade.php [deleted file]
resources/views/partials/custom-head.blade.php
resources/views/partials/entity-export-menu.blade.php
resources/views/partials/entity-export-meta.blade.php
resources/views/partials/entity-favourite-action.blade.php [new file with mode: 0644]
resources/views/partials/entity-list-item.blade.php
resources/views/partials/entity-list.blade.php
resources/views/partials/entity-meta.blade.php
resources/views/partials/entity-sibling-navigation.blade.php [new file with mode: 0644]
resources/views/partials/export-custom-head.blade.php [new file with mode: 0644]
resources/views/partials/export-styles.blade.php
resources/views/search/all.blade.php
resources/views/shelves/form.blade.php
resources/views/shelves/show.blade.php
resources/views/simple-layout.blade.php
resources/views/tri-layout.blade.php
resources/views/users/edit.blade.php
resources/views/users/index.blade.php
routes/api.php
routes/web.php
tests/Api/BooksApiTest.php
tests/Api/ChaptersApiTest.php
tests/Api/PagesApiTest.php
tests/Auth/LdapTest.php
tests/Auth/Saml2Test.php
tests/Auth/SocialAuthTest.php
tests/BrowserKitTest.php
tests/Entity/BookTest.php
tests/Entity/CommentSettingTest.php
tests/Entity/ExportTest.php
tests/Entity/PageContentTest.php
tests/Entity/PageTest.php
tests/Entity/TagTest.php
tests/ErrorTest.php
tests/FavouriteTest.php [new file with mode: 0644]
tests/HomepageTest.php
tests/OpenGraphTest.php [new file with mode: 0644]
tests/PublicActionTest.php
tests/SharedTestHelpers.php
tests/ThemeTest.php
tests/Unit/ConfigTest.php
tests/Uploads/AttachmentTest.php
tests/Uploads/ImageTest.php
version

index d243f2c1fcb9d13b52ba9dc9e55a27b07e663f79..26df8f3cb8e918102ddc73fe8fae0a812bc1d33e 100644 (file)
@@ -200,6 +200,7 @@ LDAP_TLS_INSECURE=false
 LDAP_ID_ATTRIBUTE=uid
 LDAP_EMAIL_ATTRIBUTE=mail
 LDAP_DISPLAY_NAME_ATTRIBUTE=cn
+LDAP_THUMBNAIL_ATTRIBUTE=null
 LDAP_FOLLOW_REFERRALS=true
 LDAP_DUMP_USER_DETAILS=false
 
@@ -222,6 +223,7 @@ SAML2_IDP_x509=null
 SAML2_ONELOGIN_OVERRIDES=null
 SAML2_DUMP_USER_DETAILS=false
 SAML2_AUTOLOAD_METADATA=false
+SAML2_IDP_AUTHNCONTEXT=true
 
 # SAML group sync configuration
 # Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/
index 349ad79d08087af24c0ea813fb80d48e32907b81..baaea5d92261fc0035000e4a42fe715b76bc58bf 100644 (file)
@@ -54,6 +54,7 @@ Name :: Languages
 @benediktvolke :: German
 @Baptistou :: French
 @arcoai :: Spanish
+@Jokuna :: Korean
 cipi1965 :: Italian
 Mykola Ronik (Mantikor) :: Ukrainian
 furkanoyk :: Turkish
@@ -156,3 +157,13 @@ nikservik :: Ukrainian; Russian; Polish
 HenrijsS :: Latvian
 Pascal R-B (pborgner) :: German
 Boris (Ginfred) :: Russian
+Jonas Anker Rasmussen (jonasanker) :: Danish
+Gerwin de Keijzer (gdekeijzer) :: Dutch; German; German Informal
+kometchtech :: Japanese
+Auri (Atalonica) :: Catalan
+Francesco Franchina (ffranchina) :: Italian
+Aimrane Kds (aimrane.kds) :: Arabic
+whenwesober :: Indonesian
+Rem (remkovdhoef) :: Dutch
+syn7ax69 :: Bulgarian; Turkish
+Blaade :: French
diff --git a/app/Actions/Favourite.php b/app/Actions/Favourite.php
new file mode 100644 (file)
index 0000000..107a765
--- /dev/null
@@ -0,0 +1,17 @@
+<?php namespace BookStack\Actions;
+
+use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
+
+class Favourite extends Model
+{
+    protected $fillable = ['user_id'];
+
+    /**
+     * Get the related model that can be favourited.
+     */
+    public function favouritable(): MorphTo
+    {
+        return $this->morphTo();
+    }
+}
index 5968ffe6d5ea9d875cf4fa574ac63d3d4c8d62a1..f73b2ed2d25423e55a044ac78f4a23327c5ef895 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Actions;
 
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
 
 class Tag extends Model
 {
@@ -9,10 +10,25 @@ class Tag extends Model
 
     /**
      * Get the entity that this tag belongs to
-     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
      */
-    public function entity()
+    public function entity(): MorphTo
     {
         return $this->morphTo('entity');
     }
+
+    /**
+     * Get a full URL to start a tag name search for this tag name.
+     */
+    public function nameUrl(): string
+    {
+        return url('/search?term=%5B' . urlencode($this->name) .'%5D');
+    }
+
+    /**
+     * Get a full URL to start a tag name and value search for this tag's values.
+     */
+    public function valueUrl(): string
+    {
+        return url('/search?term=%5B' . urlencode($this->name) .'%3D' . urlencode($this->value) . '%5D');
+    }
 }
index e9841293b5c72a1d20f045b81e8f95a7a534a9e0..de30900c76f2a6d4600a75f726dd9645fe7713ff 100644 (file)
@@ -1,7 +1,19 @@
 <?php namespace BookStack\Actions;
 
+use BookStack\Interfaces\Viewable;
 use BookStack\Model;
+use Illuminate\Database\Eloquent\Relations\MorphTo;
 
+/**
+ * Class View
+ * Views are stored per-item per-person within the database.
+ * They can be used to find popular items or recently viewed items
+ * at a per-person level. They do not record every view instance as an
+ * activity. Only the latest and original view times could be recognised.
+ *
+ * @property int $views
+ * @property int $user_id
+ */
 class View extends Model
 {
 
@@ -9,10 +21,37 @@ class View extends Model
 
     /**
      * Get all owning viewable models.
-     * @return \Illuminate\Database\Eloquent\Relations\MorphTo
      */
-    public function viewable()
+    public function viewable(): MorphTo
     {
         return $this->morphTo();
     }
+
+    /**
+     * Increment the current user's view count for the given viewable model.
+     */
+    public static function incrementFor(Viewable $viewable): int
+    {
+        $user = user();
+        if (is_null($user) || $user->isDefault()) {
+            return 0;
+        }
+
+        /** @var View $view */
+        $view = $viewable->views()->firstOrNew([
+            'user_id' => $user->id,
+        ], ['views' => 0]);
+
+        $view->forceFill(['views' => $view->views + 1])->save();
+
+        return $view->views;
+    }
+
+    /**
+     * Clear all views from the system.
+     */
+    public static function clearAll()
+    {
+        static::query()->truncate();
+    }
 }
diff --git a/app/Actions/ViewService.php b/app/Actions/ViewService.php
deleted file mode 100644 (file)
index f043845..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php namespace BookStack\Actions;
-
-use BookStack\Auth\Permissions\PermissionService;
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Entity;
-use BookStack\Entities\EntityProvider;
-use DB;
-use Illuminate\Support\Collection;
-
-class ViewService
-{
-    protected $view;
-    protected $permissionService;
-    protected $entityProvider;
-
-    /**
-     * ViewService constructor.
-     * @param View $view
-     * @param PermissionService $permissionService
-     * @param EntityProvider $entityProvider
-     */
-    public function __construct(View $view, PermissionService $permissionService, EntityProvider $entityProvider)
-    {
-        $this->view = $view;
-        $this->permissionService = $permissionService;
-        $this->entityProvider = $entityProvider;
-    }
-
-    /**
-     * Add a view to the given entity.
-     * @param \BookStack\Entities\Models\Entity $entity
-     * @return int
-     */
-    public function add(Entity $entity)
-    {
-        $user = user();
-        if ($user === null || $user->isDefault()) {
-            return 0;
-        }
-        $view = $entity->views()->where('user_id', '=', $user->id)->first();
-        // Add view if model exists
-        if ($view) {
-            $view->increment('views');
-            return $view->views;
-        }
-
-        // Otherwise create new view count
-        $entity->views()->save($this->view->newInstance([
-            'user_id' => $user->id,
-            'views' => 1
-        ]));
-
-        return 1;
-    }
-
-    /**
-     * Get the entities with the most views.
-     * @param int $count
-     * @param int $page
-     * @param string|array $filterModels
-     * @param string $action - used for permission checking
-     * @return Collection
-     */
-    public function getPopular(int $count = 10, int $page = 0, array $filterModels = null, string $action = 'view')
-    {
-        $skipCount = $count * $page;
-        $query = $this->permissionService
-            ->filterRestrictedEntityRelations($this->view->newQuery(), 'views', 'viewable_id', 'viewable_type', $action)
-            ->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
-            ->groupBy('viewable_id', 'viewable_type')
-            ->orderBy('view_count', 'desc');
-
-        if ($filterModels) {
-            $query->whereIn('viewable_type', $this->entityProvider->getMorphClasses($filterModels));
-        }
-
-        return $query->with('viewable')
-            ->skip($skipCount)
-            ->take($count)
-            ->get()
-            ->pluck('viewable')
-            ->filter();
-    }
-
-    /**
-     * Get all recently viewed entities for the current user.
-     */
-    public function getUserRecentlyViewed(int $count = 10, int $page = 1)
-    {
-        $user = user();
-        if ($user === null || $user->isDefault()) {
-            return collect();
-        }
-
-        $all = collect();
-        /** @var Entity $instance */
-        foreach ($this->entityProvider->all() as $name => $instance) {
-            $items = $instance::visible()->withLastView()
-                ->orderBy('last_viewed_at', 'desc')
-                ->skip($count * ($page - 1))
-                ->take($count)
-                ->get();
-            $all = $all->concat($items);
-        }
-
-        return $all->sortByDesc('last_viewed_at')->slice(0, $count);
-    }
-
-    /**
-     * Reset all view counts by deleting all views.
-     */
-    public function resetAll()
-    {
-        $this->view->truncate();
-    }
-}
index cabbfbbcbb7ac17065f0446fec76af95d9a362a0..71417aed2607d007fc0858039fe3df50e5265f86 100644 (file)
@@ -90,6 +90,11 @@ class LdapSessionGuard extends ExternalBaseSessionGuard
             $this->ldapService->syncGroups($user, $username);
         }
 
+        // Attach avatar if non-existent
+        if (is_null($user->avatar)) {
+            $this->ldapService->saveAndAttachAvatar($user, $userDetails);
+        }
+
         $this->login($user, $remember);
         return true;
     }
@@ -115,6 +120,8 @@ class LdapSessionGuard extends ExternalBaseSessionGuard
             'password' => Str::random(32),
         ];
 
-        return $this->registrationService->registerUser($details, null, false);
+        $user = $this->registrationService->registerUser($details, null, false);
+        $this->ldapService->saveAndAttachAvatar($user, $ldapUserDetails);
+        return $user;
     }
 }
index a438c098490586f44f06b607ff15892e726b60d9..2f632b0b533e8534ae44835920a5279727da7338 100644 (file)
@@ -3,7 +3,9 @@
 use BookStack\Auth\User;
 use BookStack\Exceptions\JsonDebugException;
 use BookStack\Exceptions\LdapException;
+use BookStack\Uploads\UserAvatars;
 use ErrorException;
+use Illuminate\Support\Facades\Log;
 
 /**
  * Class LdapService
@@ -14,15 +16,17 @@ class LdapService extends ExternalAuthService
 
     protected $ldap;
     protected $ldapConnection;
+    protected $userAvatars;
     protected $config;
     protected $enabled;
 
     /**
      * LdapService constructor.
      */
-    public function __construct(Ldap $ldap)
+    public function __construct(Ldap $ldap, UserAvatars $userAvatars)
     {
         $this->ldap = $ldap;
+        $this->userAvatars = $userAvatars;
         $this->config = config('services.ldap');
         $this->enabled = config('auth.method') === 'ldap';
     }
@@ -76,10 +80,13 @@ class LdapService extends ExternalAuthService
         $idAttr = $this->config['id_attribute'];
         $emailAttr = $this->config['email_attribute'];
         $displayNameAttr = $this->config['display_name_attribute'];
+        $thumbnailAttr = $this->config['thumbnail_attribute'];
 
-        $user = $this->getUserWithAttributes($userName, ['cn', 'dn', $idAttr, $emailAttr, $displayNameAttr]);
+        $user = $this->getUserWithAttributes($userName, array_filter([
+            'cn', 'dn', $idAttr, $emailAttr, $displayNameAttr, $thumbnailAttr,
+        ]));
 
-        if ($user === null) {
+        if (is_null($user)) {
             return null;
         }
 
@@ -89,6 +96,7 @@ class LdapService extends ExternalAuthService
             'name' => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
             'dn' => $user['dn'],
             'email' => $this->getUserResponseProperty($user, $emailAttr, null),
+            'avatar'=> $thumbnailAttr ? $this->getUserResponseProperty($user, $thumbnailAttr, null) : null,
         ];
 
         if ($this->config['dump_user_details']) {
@@ -350,4 +358,22 @@ class LdapService extends ExternalAuthService
         $userLdapGroups = $this->getUserGroups($username);
         $this->syncWithGroups($user, $userLdapGroups);
     }
+
+    /**
+     * Save and attach an avatar image, if found in the ldap details, and attach
+     * to the given user model.
+     */
+    public function saveAndAttachAvatar(User $user, array $ldapUserDetails): void
+    {
+        if (is_null(config('services.ldap.thumbnail_attribute')) || is_null($ldapUserDetails['avatar'])) {
+            return;
+        }
+
+        try {
+            $imageData = $ldapUserDetails['avatar'];
+            $this->userAvatars->assignToUserFromExistingData($user, $imageData, 'jpg');
+        } catch (\Exception $exception) {
+            Log::info("Failed to use avatar image from LDAP data for user id {$user->id}");
+        }
+    }
 }
index 7c8b66ea57ab6ad84b6a7aa839b6d223ee98a039..a03eb2b1d97d662421f6b41a451dc5f33674fe6a 100644 (file)
@@ -19,10 +19,37 @@ use Symfony\Component\HttpFoundation\RedirectResponse;
 
 class SocialAuthService
 {
+    /**
+     * The core socialite library used.
+     * @var Socialite
+     */
     protected $socialite;
-    protected $socialAccount;
 
-    protected $validSocialDrivers = ['google', 'github', 'facebook', 'slack', 'twitter', 'azure', 'okta', 'gitlab', 'twitch', 'discord'];
+    /**
+     * The default built-in social drivers we support.
+     * @var string[]
+     */
+    protected $validSocialDrivers = [
+        'google',
+        'github',
+        'facebook',
+        'slack',
+        'twitter',
+        'azure',
+        'okta',
+        'gitlab',
+        'twitch',
+        'discord'
+    ];
+
+    /**
+     * Callbacks to run when configuring a social driver
+     * for an initial redirect action.
+     * Array is keyed by social driver name.
+     * Callbacks are passed an instance of the driver.
+     * @var array<string, callable>
+     */
+    protected $configureForRedirectCallbacks = [];
 
     /**
      * SocialAuthService constructor.
@@ -39,7 +66,7 @@ class SocialAuthService
     public function startLogIn(string $socialDriver): RedirectResponse
     {
         $driver = $this->validateDriver($socialDriver);
-        return $this->getSocialDriver($driver)->redirect();
+        return $this->getDriverForRedirect($driver)->redirect();
     }
 
     /**
@@ -49,7 +76,7 @@ class SocialAuthService
     public function startRegister(string $socialDriver): RedirectResponse
     {
         $driver = $this->validateDriver($socialDriver);
-        return $this->getSocialDriver($driver)->redirect();
+        return $this->getDriverForRedirect($driver)->redirect();
     }
 
     /**
@@ -227,7 +254,7 @@ class SocialAuthService
     /**
      * Provide redirect options per service for the Laravel Socialite driver
      */
-    public function getSocialDriver(string $driverName): Provider
+    protected function getDriverForRedirect(string $driverName): Provider
     {
         $driver = $this->socialite->driver($driverName);
 
@@ -238,6 +265,10 @@ class SocialAuthService
             $driver->with(['resource' => 'https://graph.windows.net']);
         }
 
+        if (isset($this->configureForRedirectCallbacks[$driverName])) {
+            $this->configureForRedirectCallbacks[$driverName]($driver);
+        }
+
         return $driver;
     }
 
@@ -248,12 +279,19 @@ class SocialAuthService
      * within the `Config/services.php` file.
      * Handler should be a Class@method handler to the SocialiteWasCalled event.
      */
-    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler)
-    {
+    public function addSocialDriver(
+        string $driverName,
+        array $config,
+        string $socialiteHandler,
+        callable $configureForRedirect = null
+    ) {
         $this->validSocialDrivers[] = $driverName;
         config()->set('services.' . $driverName, $config);
         config()->set('services.' . $driverName . '.redirect', url('/login/service/' . $driverName . '/callback'));
         config()->set('services.' . $driverName . '.name', $config['name'] ?? $driverName);
         Event::listen(SocialiteWasCalled::class, $socialiteHandler);
+        if (!is_null($configureForRedirect)) {
+            $this->configureForRedirectCallbacks[$driverName] = $configureForRedirect;
+        }
     }
 }
index c5bdc8070cd6190b71e6cb876bf31e2042a8538f..4565986535d6240a9a0c80211bc61f0929117aa6 100644 (file)
@@ -580,14 +580,15 @@ class PermissionService
 
     /**
      * Filter items that have entities set as a polymorphic relation.
+     * @param Builder|\Illuminate\Database\Query\Builder $query
      */
-    public function filterRestrictedEntityRelations(Builder $query, string $tableName, string $entityIdColumn, string $entityTypeColumn, string $action = 'view'): Builder
+    public function filterRestrictedEntityRelations($query, string $tableName, string $entityIdColumn, string $entityTypeColumn, string $action = 'view')
     {
         $tableDetails = ['tableName' => $tableName, 'entityIdColumn' => $entityIdColumn, 'entityTypeColumn' => $entityTypeColumn];
 
         $q = $query->where(function ($query) use ($tableDetails, $action) {
             $query->whereExists(function ($permissionQuery) use (&$tableDetails, $action) {
-                $permissionQuery->select('id')->from('joint_permissions')
+                $permissionQuery->select(['role_id'])->from('joint_permissions')
                     ->whereRaw('joint_permissions.entity_id=' . $tableDetails['tableName'] . '.' . $tableDetails['entityIdColumn'])
                     ->whereRaw('joint_permissions.entity_type=' . $tableDetails['tableName'] . '.' . $tableDetails['entityTypeColumn'])
                     ->where('action', '=', $action)
index 629cd6a955d8abf7961b67aa1c598d1d62d30658..59b1f547c13f527b7f9bd19d524525a9415e1972 100644 (file)
@@ -92,7 +92,7 @@ class Role extends Model implements Loggable
     }
 
     /**
-     * Get all visible roles
+     * Get all visible roles.
      */
     public static function visible(): Collection
     {
@@ -104,7 +104,10 @@ class Role extends Model implements Loggable
      */
     public static function restrictable(): Collection
     {
-        return static::query()->where('system_name', '!=', 'admin')->get();
+        return static::query()
+            ->where('system_name', '!=', 'admin')
+            ->orderBy('display_name', 'asc')
+            ->get();
     }
 
     /**
index 9855ab4e7408e86403f4e5c11757afc95af68241..2f3c00a4b51346e1e333304318fed80fa2872032 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace BookStack\Auth;
 
+use BookStack\Actions\Favourite;
 use BookStack\Api\ApiToken;
 use BookStack\Entities\Tools\SlugGenerator;
 use BookStack\Interfaces\Loggable;
@@ -240,6 +241,14 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
         return $this->hasMany(ApiToken::class);
     }
 
+    /**
+     * Get the favourite instances for this user.
+     */
+    public function favourites(): HasMany
+    {
+        return $this->hasMany(Favourite::class);
+    }
+
     /**
      * Get the last activity time for this user.
      */
index e437ff1e3b367daeba67fca740197a2e5483894f..15ce2cbc9bb28d2b9aaf52208cc4fc0d488a358d 100644 (file)
@@ -8,13 +8,11 @@ use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Models\Page;
 use BookStack\Exceptions\NotFoundException;
 use BookStack\Exceptions\UserUpdateException;
-use BookStack\Uploads\Image;
 use BookStack\Uploads\UserAvatars;
 use Exception;
 use Illuminate\Database\Eloquent\Builder;
 use Illuminate\Database\Eloquent\Collection;
 use Illuminate\Pagination\LengthAwarePaginator;
-use Images;
 use Log;
 
 class UserRepo
@@ -184,16 +182,11 @@ class UserRepo
     {
         $user->socialAccounts()->delete();
         $user->apiTokens()->delete();
+        $user->favourites()->delete();
         $user->delete();
         
         // Delete user profile images
-        $profileImages = Image::query()->where('type', '=', 'user')
-            ->where('uploaded_to', '=', $user->id)
-            ->get();
-
-        foreach ($profileImages as $image) {
-            Images::destroy($image);
-        }
+        $this->userAvatar->destroyAllForUser($user);
 
         if (!empty($newOwnerId)) {
             $newOwner = User::query()->find($newOwnerId);
index 065845f961258f0fc04d0c9c46d18ddacc50f7f4..550c98b0a6f00c98a87dd2f5deb5dba76733ddf0 100755 (executable)
@@ -56,7 +56,7 @@ return [
     'locale' => env('APP_LANG', 'en'),
 
     // Locales available
-    'locales' => ['en', 'ar', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hu', 'id', 'it', 'ja', 'ko', 'lv', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl',  'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
+    'locales' => ['en', 'ar', 'bg', 'bs', 'ca', 'cs', 'da', 'de', 'de_informal', 'es', 'es_AR', 'fa', 'fr', 'he', 'hr', 'hu', 'id', 'it', 'ja', 'ko', 'lv', 'nl', 'nb', 'pt', 'pt_BR', 'sk', 'sl', 'sv', 'pl',  'ru', 'th', 'tr', 'uk', 'vi', 'zh_CN', 'zh_TW',],
 
     //  Application Fallback Locale
     'fallback_locale' => 'en',
@@ -184,11 +184,8 @@ return [
 
         // Custom BookStack
         'Activity' => BookStack\Facades\Activity::class,
-        'Views'    => BookStack\Facades\Views::class,
-        'Images'   => BookStack\Facades\Images::class,
         'Permissions' => BookStack\Facades\Permissions::class,
         'Theme'    => BookStack\Facades\Theme::class,
-
     ],
 
     // Proxy configuration
index 87be53df52b373d7c701a590f3051c7066fff711..094739cd9e22314cf67664814b5605692677d62e 100644 (file)
@@ -38,7 +38,7 @@ return [
          * Times-Roman, Times-Bold, Times-BoldItalic, Times-Italic,
          * Symbol, ZapfDingbats.
          */
-        "DOMPDF_FONT_DIR" => app_path('vendor/dompdf/dompdf/lib/fonts/'), //storage_path('fonts/'), // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
+        "DOMPDF_FONT_DIR" => storage_path('fonts/'),  // advised by dompdf (https://github.com/dompdf/dompdf/pull/782)
 
         /**
          * The location of the DOMPDF font cache directory
@@ -219,7 +219,7 @@ return [
          *
          * @var bool
          */
-        "DOMPDF_ENABLE_JAVASCRIPT" => true,
+        "DOMPDF_ENABLE_JAVASCRIPT" => false,
 
         /**
          * Enable remote file access
index d695abf325d1e5260b14d8325ea503c18bfe1abb..8ba96954905f029c5883c3b4bd5616945d9cfe5a 100644 (file)
@@ -1,5 +1,7 @@
 <?php
 
+$SAML2_IDP_AUTHNCONTEXT = env('SAML2_IDP_AUTHNCONTEXT', true);
+
 return [
 
     // Display name, shown to users, for SAML2 option
@@ -139,6 +141,14 @@ return [
             //      )
             // ),
         ],
+        'security' => [
+            // SAML2 Authn context
+            // When set to false no AuthContext will be sent in the AuthNRequest,
+            // When set to true (Default) you will get an AuthContext 'exact' 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'.
+            // Multiple forced values can be passed via a space separated array, For example:
+            // SAML2_IDP_AUTHNCONTEXT="urn:federation:authentication:windows urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
+            'requestedAuthnContext' => is_string($SAML2_IDP_AUTHNCONTEXT) ? explode(' ', $SAML2_IDP_AUTHNCONTEXT) : $SAML2_IDP_AUTHNCONTEXT,
+        ],
     ],
 
 ];
index 6993396147af9b02eec0a778c81b7bb83a6764c3..0f9f78d1e53eda3e4625fafdd8e3c964ef1a728e 100644 (file)
@@ -133,6 +133,7 @@ return [
         'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false),
         'tls_insecure' => env('LDAP_TLS_INSECURE', false),
         'start_tls' => env('LDAP_START_TLS', false),
+        'thumbnail_attribute' => env('LDAP_THUMBNAIL_ATTRIBUTE', null),
     ],
 
 ];
index 35356210b66809c62687e8fbb1eaa437bc447106..693d9363973e8229bb37a3037db667de862b1f85 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace BookStack\Console\Commands;
 
+use BookStack\Actions\View;
 use Illuminate\Console\Command;
 
 class ClearViews extends Command
@@ -36,7 +37,7 @@ class ClearViews extends Command
      */
     public function handle()
     {
-        \Views::resetAll();
+        View::clearAll();
         $this->comment('Views cleared');
     }
 }
index 257b19e37e45afe981ffeb7cf6ccfcd82cdefec3..1810685335bf6910dc2486eb6410fa47e88edcca 100644 (file)
@@ -5,6 +5,7 @@ use Illuminate\Support\Collection;
 /**
  * Class Chapter
  * @property Collection<Page> $pages
+ * @property mixed description
  */
 class Chapter extends BookChild
 {
index d4b477304da8848d113b3882517a7d9bcea366f5..5618767693b34311ee6057fae86dde062a1192e0 100644 (file)
@@ -2,6 +2,7 @@
 
 use BookStack\Actions\Activity;
 use BookStack\Actions\Comment;
+use BookStack\Actions\Favourite;
 use BookStack\Actions\Tag;
 use BookStack\Actions\View;
 use BookStack\Auth\Permissions\EntityPermission;
@@ -9,7 +10,9 @@ use BookStack\Auth\Permissions\JointPermission;
 use BookStack\Entities\Tools\SearchIndex;
 use BookStack\Entities\Tools\SlugGenerator;
 use BookStack\Facades\Permissions;
+use BookStack\Interfaces\Favouritable;
 use BookStack\Interfaces\Sluggable;
+use BookStack\Interfaces\Viewable;
 use BookStack\Model;
 use BookStack\Traits\HasCreatorAndUpdater;
 use BookStack\Traits\HasOwner;
@@ -38,7 +41,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
  * @method static Builder withLastView()
  * @method static Builder withViewCount()
  */
-abstract class Entity extends Model implements Sluggable
+abstract class Entity extends Model implements Sluggable, Favouritable, Viewable
 {
     use SoftDeletes;
     use HasCreatorAndUpdater;
@@ -297,4 +300,22 @@ abstract class Entity extends Model implements Sluggable
         $this->slug = app(SlugGenerator::class)->generate($this);
         return $this->slug;
     }
+
+    /**
+     * @inheritdoc
+     */
+    public function favourites(): MorphMany
+    {
+        return $this->morphMany(Favourite::class, 'favouritable');
+    }
+
+    /**
+     * Check if the entity is a favourite of the current user.
+     */
+    public function isFavourite(): bool
+    {
+        return $this->favourites()
+            ->where('user_id', '=', user()->id)
+            ->exists();
+    }
 }
index 7e397894de35fec52bd6692bf74017eed54cd45a..93fb218932cf6e9f2796fce7f3aaac8586c43490 100644 (file)
@@ -75,11 +75,23 @@ class Page extends BookChild
 
     /**
      * Get the associated page revisions, ordered by created date.
-     * @return mixed
+     * Only provides actual saved page revision instances, Not drafts.
+     */
+    public function revisions(): HasMany
+    {
+        return $this->allRevisions()
+            ->where('type', '=', 'version')
+            ->orderBy('created_at', 'desc')
+            ->orderBy('id', 'desc');
+    }
+
+    /**
+     * Get all revision instances assigned to this page.
+     * Includes all types of revisions.
      */
-    public function revisions()
+    public function allRevisions(): HasMany
     {
-        return $this->hasMany(PageRevision::class)->where('type', '=', 'version')->orderBy('created_at', 'desc')->orderBy('id', 'desc');
+        return $this->hasMany(PageRevision::class);
     }
 
     /**
diff --git a/app/Entities/Queries/EntityQuery.php b/app/Entities/Queries/EntityQuery.php
new file mode 100644 (file)
index 0000000..bd920c3
--- /dev/null
@@ -0,0 +1,17 @@
+<?php namespace BookStack\Entities\Queries;
+
+use BookStack\Auth\Permissions\PermissionService;
+use BookStack\Entities\EntityProvider;
+
+abstract class EntityQuery
+{
+    protected function permissionService(): PermissionService
+    {
+        return app()->make(PermissionService::class);
+    }
+
+    protected function entityProvider(): EntityProvider
+    {
+        return app()->make(EntityProvider::class);
+    }
+}
\ No newline at end of file
diff --git a/app/Entities/Queries/Popular.php b/app/Entities/Queries/Popular.php
new file mode 100644 (file)
index 0000000..98db2fe
--- /dev/null
@@ -0,0 +1,29 @@
+<?php namespace BookStack\Entities\Queries;
+
+
+use BookStack\Actions\View;
+use Illuminate\Support\Facades\DB;
+
+class Popular extends EntityQuery
+{
+    public function run(int $count, int $page, array $filterModels = null, string $action = 'view')
+    {
+        $query = $this->permissionService()
+            ->filterRestrictedEntityRelations(View::query(), 'views', 'viewable_id', 'viewable_type', $action)
+            ->select('*', 'viewable_id', 'viewable_type', DB::raw('SUM(views) as view_count'))
+            ->groupBy('viewable_id', 'viewable_type')
+            ->orderBy('view_count', 'desc');
+
+        if ($filterModels) {
+            $query->whereIn('viewable_type', $this->entityProvider()->getMorphClasses($filterModels));
+        }
+
+        return $query->with('viewable')
+            ->skip($count * ($page - 1))
+            ->take($count)
+            ->get()
+            ->pluck('viewable')
+            ->filter();
+    }
+
+}
\ No newline at end of file
diff --git a/app/Entities/Queries/RecentlyViewed.php b/app/Entities/Queries/RecentlyViewed.php
new file mode 100644 (file)
index 0000000..d528fea
--- /dev/null
@@ -0,0 +1,32 @@
+<?php namespace BookStack\Entities\Queries;
+
+use BookStack\Actions\View;
+use Illuminate\Support\Collection;
+
+class RecentlyViewed extends EntityQuery
+{
+    public function run(int $count, int $page): Collection
+    {
+        $user = user();
+        if ($user === null || $user->isDefault()) {
+            return collect();
+        }
+
+        $query = $this->permissionService()->filterRestrictedEntityRelations(
+            View::query(),
+            'views',
+            'viewable_id',
+            'viewable_type',
+            'view'
+        )
+            ->orderBy('views.updated_at', 'desc')
+            ->where('user_id', '=', user()->id);
+
+        return $query->with('viewable')
+            ->skip(($page - 1) * $count)
+            ->take($count)
+            ->get()
+            ->pluck('viewable')
+            ->filter();
+    }
+}
diff --git a/app/Entities/Queries/TopFavourites.php b/app/Entities/Queries/TopFavourites.php
new file mode 100644 (file)
index 0000000..1434180
--- /dev/null
@@ -0,0 +1,33 @@
+<?php namespace BookStack\Entities\Queries;
+
+use BookStack\Actions\Favourite;
+use Illuminate\Database\Query\JoinClause;
+
+class TopFavourites extends EntityQuery
+{
+    public function run(int $count, int $skip = 0)
+    {
+        $user = user();
+        if (is_null($user) || $user->isDefault()) {
+            return collect();
+        }
+
+        $query = $this->permissionService()
+            ->filterRestrictedEntityRelations(Favourite::query(), 'favourites', 'favouritable_id', 'favouritable_type', 'view')
+            ->select('favourites.*')
+            ->leftJoin('views', function (JoinClause $join) {
+                $join->on('favourites.favouritable_id', '=', 'views.viewable_id');
+                $join->on('favourites.favouritable_type', '=', 'views.viewable_type');
+                $join->where('views.user_id', '=', user()->id);
+            })
+            ->orderBy('views.views', 'desc')
+            ->where('favourites.user_id', '=', user()->id);
+
+        return $query->with('favouritable')
+            ->skip($skip)
+            ->take($count)
+            ->get()
+            ->pluck('favouritable')
+            ->filter();
+    }
+}
index 6a4eaeb1553f834ccafdd3e6b642d160e96d81d3..5eb882a026a3f803369b56b06b6334c46e8773c9 100644 (file)
@@ -212,7 +212,7 @@ class PageRepo
         if (!empty($input['markdown'] ?? '')) {
             $pageContent->setNewMarkdown($input['markdown']);
         } else {
-            $pageContent->setNewHTML($input['html']);
+            $pageContent->setNewHTML($input['html'] ?? '');
         }
     }
 
index eb8f6862f23fe76b703c8f0a2514b2f5d61acae9..9317b04316b5b4bb4c3d4feac62977e42785b0c3 100644 (file)
@@ -3,6 +3,7 @@
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Models\Page;
+use BookStack\Entities\Tools\Markdown\HtmlToMarkdown;
 use BookStack\Uploads\ImageService;
 use DomPDF;
 use Exception;
@@ -226,4 +227,46 @@ class ExportFormatter
         }
         return $text;
     }
+
+    /**
+     * Convert a page to a Markdown file.
+     */
+    public function pageToMarkdown(Page $page): string
+    {
+        if ($page->markdown) {
+            return "# " . $page->name . "\n\n" . $page->markdown;
+        }
+
+        return "# " . $page->name . "\n\n" . (new HtmlToMarkdown($page->html))->convert();
+    }
+
+    /**
+     * Convert a chapter to a Markdown file.
+     */
+    public function chapterToMarkdown(Chapter $chapter): string
+    {
+        $text = "# " . $chapter->name . "\n\n";
+        $text .= $chapter->description . "\n\n";
+        foreach ($chapter->pages as $page) {
+            $text .= $this->pageToMarkdown($page) . "\n\n";
+        }
+        return $text;
+    }
+
+    /**
+     * Convert a book into a plain text string.
+     */
+    public function bookToMarkdown(Book $book): string
+    {
+        $bookTree = (new BookContents($book))->getTree(false, true);
+        $text = "# " . $book->name . "\n\n";
+        foreach ($bookTree as $bookChild) {
+            if ($bookChild instanceof Chapter) {
+                $text .= $this->chapterToMarkdown($bookChild);
+            } else {
+                $text .= $this->pageToMarkdown($bookChild);
+            }
+        }
+        return $text;
+    }
 }
diff --git a/app/Entities/Tools/Markdown/CustomParagraphConverter.php b/app/Entities/Tools/Markdown/CustomParagraphConverter.php
new file mode 100644 (file)
index 0000000..1af7702
--- /dev/null
@@ -0,0 +1,17 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\HTMLToMarkdown\Converter\ParagraphConverter;
+use League\HTMLToMarkdown\ElementInterface;
+
+class CustomParagraphConverter extends ParagraphConverter
+{
+    public function convert(ElementInterface $element): string
+    {
+        $class = $element->getAttribute('class');
+        if (strpos($class, 'callout') !== false) {
+            return "<{$element->getTagName()} class=\"{$class}\">{$element->getValue()}</{$element->getTagName()}>\n\n";
+        }
+
+        return parent::convert($element);
+    }
+}
diff --git a/app/Entities/Tools/Markdown/HtmlToMarkdown.php b/app/Entities/Tools/Markdown/HtmlToMarkdown.php
new file mode 100644 (file)
index 0000000..c56119f
--- /dev/null
@@ -0,0 +1,90 @@
+<?php namespace BookStack\Entities\Tools\Markdown;
+
+use League\HTMLToMarkdown\Converter\BlockquoteConverter;
+use League\HTMLToMarkdown\Converter\CodeConverter;
+use League\HTMLToMarkdown\Converter\CommentConverter;
+use League\HTMLToMarkdown\Converter\DivConverter;
+use League\HTMLToMarkdown\Converter\EmphasisConverter;
+use League\HTMLToMarkdown\Converter\HardBreakConverter;
+use League\HTMLToMarkdown\Converter\HeaderConverter;
+use League\HTMLToMarkdown\Converter\HorizontalRuleConverter;
+use League\HTMLToMarkdown\Converter\ImageConverter;
+use League\HTMLToMarkdown\Converter\LinkConverter;
+use League\HTMLToMarkdown\Converter\ListBlockConverter;
+use League\HTMLToMarkdown\Converter\ListItemConverter;
+use League\HTMLToMarkdown\Converter\PreformattedConverter;
+use League\HTMLToMarkdown\Converter\TextConverter;
+use League\HTMLToMarkdown\Environment;
+use League\HTMLToMarkdown\HtmlConverter;
+
+class HtmlToMarkdown
+{
+    protected $html;
+
+    public function __construct(string $html)
+    {
+        $this->html = $html;
+    }
+
+    /**
+     * Run the conversion
+     */
+    public function convert(): string
+    {
+        $converter = new HtmlConverter($this->getConverterEnvironment());
+        $html = $this->prepareHtml($this->html);
+        return $converter->convert($html);
+    }
+
+    /**
+     * Run any pre-processing to the HTML to clean it up manually before conversion.
+     */
+    protected function prepareHtml(string $html): string
+    {
+        // Carriage returns can cause whitespace issues in output
+        $html = str_replace("\r\n", "\n", $html);
+        // Attributes on the pre tag can cause issues with conversion
+        return preg_replace('/<pre .*?>/', '<pre>', $html);
+    }
+
+    /**
+     * Get the HTML to Markdown customized environment.
+     * Extends the default provided environment with some BookStack specific tweaks.
+     */
+    protected function getConverterEnvironment(): Environment
+    {
+        $environment = new Environment([
+            'header_style' => 'atx', // Set to 'atx' to output H1 and H2 headers as # Header1 and ## Header2
+            'suppress_errors' => true, // Set to false to show warnings when loading malformed HTML
+            'strip_tags' => false, // Set to true to strip tags that don't have markdown equivalents. N.B. Strips tags, not their content. Useful to clean MS Word HTML output.
+            'strip_placeholder_links' => false, // Set to true to remove <a> that doesn't have href.
+            'bold_style' => '**', // DEPRECATED: Set to '__' if you prefer the underlined style
+            'italic_style' => '*', // DEPRECATED: Set to '_' if you prefer the underlined style
+            'remove_nodes' => '', // space-separated list of dom nodes that should be removed. example: 'meta style script'
+            'hard_break' => false, // Set to true to turn <br> into `\n` instead of `  \n`
+            'list_item_style' => '-', // Set the default character for each <li> in a <ul>. Can be '-', '*', or '+'
+            'preserve_comments' => false, // Set to true to preserve comments, or set to an array of strings to preserve specific comments
+            'use_autolinks' => false, // Set to true to use simple link syntax if possible. Will always use []() if set to false
+            'table_pipe_escape' => '\|', // Replacement string for pipe characters inside markdown table cells
+            'table_caption_side' => 'top', // Set to 'top' or 'bottom' to show <caption> content before or after table, null to suppress
+        ]);
+
+        $environment->addConverter(new BlockquoteConverter());
+        $environment->addConverter(new CodeConverter());
+        $environment->addConverter(new CommentConverter());
+        $environment->addConverter(new DivConverter());
+        $environment->addConverter(new EmphasisConverter());
+        $environment->addConverter(new HardBreakConverter());
+        $environment->addConverter(new HeaderConverter());
+        $environment->addConverter(new HorizontalRuleConverter());
+        $environment->addConverter(new ImageConverter());
+        $environment->addConverter(new LinkConverter());
+        $environment->addConverter(new ListBlockConverter());
+        $environment->addConverter(new ListItemConverter());
+        $environment->addConverter(new CustomParagraphConverter());
+        $environment->addConverter(new PreformattedConverter());
+        $environment->addConverter(new TextConverter());
+
+        return $environment;
+    }
+}
diff --git a/app/Entities/Tools/NextPreviousContentLocator.php b/app/Entities/Tools/NextPreviousContentLocator.php
new file mode 100644 (file)
index 0000000..bfb0f4a
--- /dev/null
@@ -0,0 +1,69 @@
+<?php namespace BookStack\Entities\Tools;
+
+use BookStack\Entities\Models\BookChild;
+use BookStack\Entities\Models\Entity;
+use Illuminate\Support\Collection;
+
+/**
+ * Finds the next or previous content of a book element (page or chapter).
+ */
+class NextPreviousContentLocator
+{
+    protected $relativeBookItem;
+    protected $flatTree;
+    protected $currentIndex = null;
+
+    /**
+     * NextPreviousContentLocator constructor.
+     */
+    public function __construct(BookChild $relativeBookItem, Collection $bookTree)
+    {
+        $this->relativeBookItem = $relativeBookItem;
+        $this->flatTree = $this->treeToFlatOrderedCollection($bookTree);
+        $this->currentIndex = $this->getCurrentIndex();
+    }
+
+    /**
+     * Get the next logical entity within the book hierarchy.
+     */
+    public function getNext(): ?Entity
+    {
+        return $this->flatTree->get($this->currentIndex + 1);
+    }
+
+    /**
+     * Get the next logical entity within the book hierarchy.
+     */
+    public function getPrevious(): ?Entity
+    {
+        return $this->flatTree->get($this->currentIndex - 1);
+    }
+
+    /**
+     * Get the index of the current relative item.
+     */
+    protected function getCurrentIndex(): ?int
+    {
+        $index = $this->flatTree->search(function (Entity $entity) {
+            return get_class($entity) === get_class($this->relativeBookItem)
+                && $entity->id === $this->relativeBookItem->id;
+        });
+        return $index === false ? null : $index;
+    }
+
+    /**
+     * Convert a book tree collection to a flattened version
+     * where all items follow the expected order of user flow.
+     */
+    protected function treeToFlatOrderedCollection(Collection $bookTree): Collection
+    {
+        $flatOrdered = collect();
+        /** @var Entity $item */
+        foreach ($bookTree->all() as $item) {
+            $flatOrdered->push($item);
+            $childPages = $item->visible_pages ?? [];
+            $flatOrdered = $flatOrdered->concat($childPages);
+        }
+        return $flatOrdered;
+    }
+}
index 82499cdf2d28030f5930e5f4e94fff11f8a3728a..381ef172b47105939740a31ae807a7fbff865f10 100644 (file)
@@ -2,11 +2,15 @@
 
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Tools\Markdown\CustomStrikeThroughExtension;
+use BookStack\Exceptions\ImageUploadException;
 use BookStack\Facades\Theme;
 use BookStack\Theming\ThemeEvents;
+use BookStack\Util\HtmlContentFilter;
+use BookStack\Uploads\ImageRepo;
 use DOMDocument;
 use DOMNodeList;
 use DOMXPath;
+use Illuminate\Support\Str;
 use League\CommonMark\CommonMarkConverter;
 use League\CommonMark\Environment;
 use League\CommonMark\Extension\Table\TableExtension;
@@ -30,6 +34,7 @@ class PageContent
      */
     public function setNewHTML(string $html)
     {
+        $html = $this->extractBase64Images($this->page, $html);
         $this->page->html = $this->formatHtml($html);
         $this->page->text = $this->toPlainText();
         $this->page->markdown = '';
@@ -60,19 +65,65 @@ class PageContent
         return $converter->convertToHtml($markdown);
     }
 
+    /**
+     * Convert all base64 image data to saved images
+     */
+    public function extractBase64Images(Page $page, string $htmlText): string
+    {
+        if (empty($htmlText) || strpos($htmlText, 'data:image') === false) {
+            return $htmlText;
+        }
+
+        $doc = $this->loadDocumentFromHtml($htmlText);
+        $container = $doc->documentElement;
+        $body = $container->childNodes->item(0);
+        $childNodes = $body->childNodes;
+        $xPath = new DOMXPath($doc);
+        $imageRepo = app()->make(ImageRepo::class);
+        $allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
+
+        // Get all img elements with image data blobs
+        $imageNodes = $xPath->query('//img[contains(@src, \'data:image\')]');
+        foreach ($imageNodes as $imageNode) {
+            $imageSrc = $imageNode->getAttribute('src');
+            [$dataDefinition, $base64ImageData] = explode(',', $imageSrc, 2);
+            $extension = strtolower(preg_split('/[\/;]/', $dataDefinition)[1] ?? 'png');
+
+            // Validate extension
+            if (!in_array($extension, $allowedExtensions)) {
+                $imageNode->setAttribute('src', '');
+                continue;
+            }
+
+            // Save image from data with a random name
+            $imageName = 'embedded-image-' . Str::random(8) . '.' . $extension;
+            try {
+                $image = $imageRepo->saveNewFromData($imageName, base64_decode($base64ImageData), 'gallery', $page->id);
+                $imageNode->setAttribute('src', $image->url);
+            } catch (ImageUploadException $exception) {
+                $imageNode->setAttribute('src', '');
+            }
+        }
+
+        // Generate inner html as a string
+        $html = '';
+        foreach ($childNodes as $childNode) {
+            $html .= $doc->saveHTML($childNode);
+        }
+
+        return $html;
+    }
+
     /**
      * Formats a page's html to be tagged correctly within the system.
      */
     protected function formatHtml(string $htmlText): string
     {
-        if ($htmlText == '') {
+        if (empty($htmlText)) {
             return $htmlText;
         }
 
-        libxml_use_internal_errors(true);
-        $doc = new DOMDocument();
-        $doc->loadHTML(mb_convert_encoding($htmlText, 'HTML-ENTITIES', 'UTF-8'));
-
+        $doc = $this->loadDocumentFromHtml($htmlText);
         $container = $doc->documentElement;
         $body = $container->childNodes->item(0);
         $childNodes = $body->childNodes;
@@ -111,7 +162,7 @@ class PageContent
     protected function updateLinks(DOMXPath $xpath, string $old, string $new)
     {
         $old = str_replace('"', '', $old);
-        $matchingLinks = $xpath->query('//body//*//*[@href="'.$old.'"]');
+        $matchingLinks = $xpath->query('//body//*//*[@href="' . $old . '"]');
         foreach ($matchingLinks as $domElem) {
             $domElem->setAttribute('href', $new);
         }
@@ -164,12 +215,12 @@ class PageContent
     /**
      * Render the page for viewing
      */
-    public function render(bool $blankIncludes = false) : string
+    public function render(bool $blankIncludes = false): string
     {
         $content = $this->page->html;
 
         if (!config('app.allow_content_scripts')) {
-            $content = $this->escapeScripts($content);
+            $content = HtmlContentFilter::removeScripts($content);
         }
 
         if ($blankIncludes) {
@@ -190,9 +241,7 @@ class PageContent
             return [];
         }
 
-        libxml_use_internal_errors(true);
-        $doc = new DOMDocument();
-        $doc->loadHTML(mb_convert_encoding($htmlContent, 'HTML-ENTITIES', 'UTF-8'));
+        $doc = $this->loadDocumentFromHtml($htmlContent);
         $xPath = new DOMXPath($doc);
         $headers = $xPath->query("//h1|//h2|//h3|//h4|//h5|//h6");
 
@@ -232,7 +281,7 @@ class PageContent
     /**
      * Remove any page include tags within the given HTML.
      */
-    protected function blankPageIncludes(string $html) : string
+    protected function blankPageIncludes(string $html): string
     {
         return preg_replace("/{{@\s?([0-9].*?)}}/", '', $html);
     }
@@ -240,7 +289,7 @@ class PageContent
     /**
      * Parse any include tags "{{@<page_id>#section}}" to be part of the page.
      */
-    protected function parsePageIncludes(string $html) : string
+    protected function parsePageIncludes(string $html): string
     {
         $matches = [];
         preg_match_all("/{{@\s?([0-9].*?)}}/", $html, $matches);
@@ -283,9 +332,7 @@ class PageContent
     protected function fetchSectionOfPage(Page $page, string $sectionId): string
     {
         $topLevelTags = ['table', 'ul', 'ol'];
-        $doc = new DOMDocument();
-        libxml_use_internal_errors(true);
-        $doc->loadHTML(mb_convert_encoding('<body>'.$page->html.'</body>', 'HTML-ENTITIES', 'UTF-8'));
+        $doc = $this->loadDocumentFromHtml($page->html);
 
         // Search included content for the id given and blank out if not exists.
         $matchingElem = $doc->getElementById($sectionId);
@@ -310,63 +357,14 @@ class PageContent
     }
 
     /**
-     * Escape script tags within HTML content.
+     * Create and load a DOMDocument from the given html content.
      */
-    protected function escapeScripts(string $html) : string
+    protected function loadDocumentFromHtml(string $html): DOMDocument
     {
-        if (empty($html)) {
-            return $html;
-        }
-
         libxml_use_internal_errors(true);
         $doc = new DOMDocument();
+        $html = '<body>' . $html . '</body>';
         $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
-        $xPath = new DOMXPath($doc);
-
-        // Remove standard script tags
-        $scriptElems = $xPath->query('//script');
-        foreach ($scriptElems as $scriptElem) {
-            $scriptElem->parentNode->removeChild($scriptElem);
-        }
-
-        // Remove clickable links to JavaScript URI
-        $badLinks = $xPath->query('//*[contains(@href, \'javascript:\')]');
-        foreach ($badLinks as $badLink) {
-            $badLink->parentNode->removeChild($badLink);
-        }
-
-        // Remove forms with calls to JavaScript URI
-        $badForms = $xPath->query('//*[contains(@action, \'javascript:\')] | //*[contains(@formaction, \'javascript:\')]');
-        foreach ($badForms as $badForm) {
-            $badForm->parentNode->removeChild($badForm);
-        }
-
-        // Remove meta tag to prevent external redirects
-        $metaTags = $xPath->query('//meta[contains(@content, \'url\')]');
-        foreach ($metaTags as $metaTag) {
-            $metaTag->parentNode->removeChild($metaTag);
-        }
-
-        // Remove data or JavaScript iFrames
-        $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]');
-        foreach ($badIframes as $badIframe) {
-            $badIframe->parentNode->removeChild($badIframe);
-        }
-
-        // Remove 'on*' attributes
-        $onAttributes = $xPath->query('//@*[starts-with(name(), \'on\')]');
-        foreach ($onAttributes as $attr) {
-            /** @var \DOMAttr $attr*/
-            $attrName = $attr->nodeName;
-            $attr->parentNode->removeAttribute($attrName);
-        }
-
-        $html = '';
-        $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
-        foreach ($topElems as $child) {
-            $html .= $doc->saveHTML($child);
-        }
-
-        return $html;
+        return $doc;
     }
 }
index df98fd318fd89e1b0d82b897cbcd038a97a4e1a3..0b6081ae46a1208cc9d74b384b61a1c15f5efde3 100644 (file)
@@ -151,6 +151,7 @@ class TrashCan
     protected function destroyPage(Page $page): int
     {
         $this->destroyCommonRelations($page);
+        $page->allRevisions()->delete();
 
         // Delete Attached Files
         $attachmentService = app(AttachmentService::class);
@@ -317,6 +318,7 @@ class TrashCan
         $entity->jointPermissions()->delete();
         $entity->searchTerms()->delete();
         $entity->deletions()->delete();
+        $entity->favourites()->delete();
 
         if ($entity instanceof HasCoverImage && $entity->cover) {
             $imageService = app()->make(ImageService::class);
index 19689716431bcaa2780a79ba089fab2f508e17a7..a06c0fdbc103821792a62c86625354096d05a481 100644 (file)
@@ -9,7 +9,6 @@ use Illuminate\Http\JsonResponse;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 class Handler extends ExceptionHandler
 {
@@ -58,29 +57,6 @@ class Handler extends ExceptionHandler
             return $this->renderApiException($e);
         }
 
-        // Handle notify exceptions which will redirect to the
-        // specified location then show a notification message.
-        if ($this->isExceptionType($e, NotifyException::class)) {
-            $message = $this->getOriginalMessage($e);
-            if (!empty($message)) {
-                session()->flash('error', $message);
-            }
-            return redirect($e->redirectLocation);
-        }
-
-        // Handle pretty exceptions which will show a friendly application-fitting page
-        // Which will include the basic message to point the user roughly to the cause.
-        if ($this->isExceptionType($e, PrettyException::class)  && !config('app.debug')) {
-            $message = $this->getOriginalMessage($e);
-            $code = ($e->getCode() === 0) ? 500 : $e->getCode();
-            return response()->view('errors/' . $code, ['message' => $message], $code);
-        }
-
-        // Handle 404 errors with a loaded session to enable showing user-specific information
-        if ($this->isExceptionType($e, NotFoundHttpException::class)) {
-            return \Route::respondWithRoute('fallback');
-        }
-
         return parent::render($request, $e);
     }
 
@@ -119,30 +95,6 @@ class Handler extends ExceptionHandler
         return new JsonResponse($responseData, $code, $headers);
     }
 
-    /**
-     * Check the exception chain to compare against the original exception type.
-     */
-    protected function isExceptionType(Exception $e, string $type): bool
-    {
-        do {
-            if (is_a($e, $type)) {
-                return true;
-            }
-        } while ($e = $e->getPrevious());
-        return false;
-    }
-
-    /**
-     * Get original exception message.
-     */
-    protected function getOriginalMessage(Exception $e): string
-    {
-        do {
-            $message = $e->getMessage();
-        } while ($e = $e->getPrevious());
-        return $message;
-    }
-
     /**
      * Convert an authentication exception into an unauthenticated response.
      *
index 4f810596099cf2132093677b571ce40bf44a5aa8..efca625708b75ef2509d4f9790664c8ab3c18d24 100644 (file)
@@ -1,8 +1,10 @@
 <?php namespace BookStack\Exceptions;
 
-class NotifyException extends \Exception
-{
+use Exception;
+use Illuminate\Contracts\Support\Responsable;
 
+class NotifyException extends Exception implements Responsable
+{
     public $message;
     public $redirectLocation;
 
@@ -15,4 +17,19 @@ class NotifyException extends \Exception
         $this->redirectLocation = $redirectLocation;
         parent::__construct();
     }
+
+    /**
+     * Send the response for this type of exception.
+     * @inheritdoc
+     */
+    public function toResponse($request)
+    {
+        $message = $this->getMessage();
+
+        if (!empty($message)) {
+            session()->flash('error', $message);
+        }
+
+        return redirect($this->redirectLocation);
+    }
 }
index 7fad7df45812e1fc2e75881e50af063b07c23ff5..8ed135de7c11f0e3c0b89a3845e4dc4d77cfca1c 100644 (file)
@@ -1,6 +1,43 @@
 <?php namespace BookStack\Exceptions;
 
-class PrettyException extends \Exception
+use Exception;
+use Illuminate\Contracts\Support\Responsable;
+
+class PrettyException extends Exception implements Responsable
 {
+    /**
+     * @var ?string
+     */
+    protected $subtitle = null;
+
+    /**
+     * @var ?string
+     */
+    protected $details = null;
+
+    /**
+     * Render a response for when this exception occurs.
+     * @inheritdoc
+     */
+    public function toResponse($request)
+    {
+        $code = ($this->getCode() === 0) ? 500 : $this->getCode();
+        return response()->view('errors.' . $code, [
+            'message' => $this->getMessage(),
+            'subtitle' => $this->subtitle,
+            'details' => $this->details,
+        ], $code);
+    }
+
+    public function setSubtitle(string $subtitle): self
+    {
+        $this->subtitle = $subtitle;
+        return $this;
+    }
 
+    public function setDetails(string $details): self
+    {
+        $this->details = $details;
+        return $this;
+    }
 }
diff --git a/app/Facades/Images.php b/app/Facades/Images.php
deleted file mode 100644 (file)
index fdbd35a..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php namespace BookStack\Facades;
-
-use Illuminate\Support\Facades\Facade;
-
-class Images extends Facade
-{
-    /**
-     * Get the registered name of the component.
-     *
-     * @return string
-     */
-    protected static function getFacadeAccessor()
-    {
-        return 'images';
-    }
-}
diff --git a/app/Facades/Views.php b/app/Facades/Views.php
deleted file mode 100644 (file)
index f535711..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php namespace BookStack\Facades;
-
-use Illuminate\Support\Facades\Facade;
-
-class Views extends Facade
-{
-    /**
-     * Get the registered name of the component.
-     *
-     * @return string
-     */
-    protected static function getFacadeAccessor()
-    {
-        return 'views';
-    }
-}
index 3d813c4d4225fabdaa686d9b15881871f85303ad..24cba9df374a83ba00f1aa9fbefb43594f8d7499 100644 (file)
@@ -44,4 +44,14 @@ class BookExportApiController extends ApiController
         $textContent = $this->exportFormatter->bookToPlainText($book);
         return $this->downloadResponse($textContent, $book->slug . '.txt');
     }
+
+    /**
+     * Export a book as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $book = Book::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->bookToMarkdown($book);
+        return $this->downloadResponse($markdown, $book->slug . '.md');
+    }
 }
index afdfe555dd56f1bee4bc0a40139476b8a2d80c4f..a4c349f4ef4aa06276adf98089531566a4c25617 100644 (file)
@@ -2,7 +2,6 @@
 
 use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Tools\ExportFormatter;
-use BookStack\Entities\Repos\BookRepo;
 use Throwable;
 
 class ChapterExportApiController extends ApiController
@@ -48,4 +47,14 @@ class ChapterExportApiController extends ApiController
         $textContent = $this->exportFormatter->chapterToPlainText($chapter);
         return $this->downloadResponse($textContent, $chapter->slug . '.txt');
     }
+
+    /**
+     * Export a chapter as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $chapter = Chapter::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->chapterToMarkdown($chapter);
+        return $this->downloadResponse($markdown, $chapter->slug . '.md');
+    }
 }
index a6db0583380e610626cef1794e8e5d8a2e501ea1..fd4a16effeb56dce464f9ebeef2a427eb6ecce3e 100644 (file)
@@ -60,6 +60,8 @@ class PageApiController extends ApiController
      *
      * Any HTML content provided should be kept to a single-block depth of plain HTML
      * elements to remain compatible with the BookStack front-end and editors.
+     * Any images included via base64 data URIs will be extracted and saved as gallery
+     * images against the page during upload.
      */
     public function create(Request $request)
     {
index 7563092cb36671c81ce826fae220b55fb5247c15..bf43016c2258d137d33fc6248d3b4021330e0c27 100644 (file)
@@ -44,4 +44,14 @@ class PageExportApiController extends ApiController
         $textContent = $this->exportFormatter->pageToPlainText($page);
         return $this->downloadResponse($textContent, $page->slug . '.txt');
     }
+
+    /**
+     * Export a page as a markdown file.
+     */
+    public function exportMarkdown(int $id)
+    {
+        $page = Page::visible()->findOrFail($id);
+        $markdown = $this->exportFormatter->pageToMarkdown($page);
+        return $this->downloadResponse($markdown, $page->slug . '.md');
+    }
 }
index 04e89ac5d1a0db18407398aabd3689951822eee8..be20cda93de55506206f6305f5c07b414de5b6e9 100644 (file)
@@ -14,16 +14,14 @@ use Illuminate\Validation\ValidationException;
 class AttachmentController extends Controller
 {
     protected $attachmentService;
-    protected $attachment;
     protected $pageRepo;
 
     /**
      * AttachmentController constructor.
      */
-    public function __construct(AttachmentService $attachmentService, Attachment $attachment, PageRepo $pageRepo)
+    public function __construct(AttachmentService $attachmentService, PageRepo $pageRepo)
     {
         $this->attachmentService = $attachmentService;
-        $this->attachment = $attachment;
         $this->pageRepo = $pageRepo;
     }
 
@@ -67,7 +65,7 @@ class AttachmentController extends Controller
             'file' => 'required|file'
         ]);
 
-        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         $this->checkOwnablePermission('view', $attachment->page);
         $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
@@ -89,7 +87,7 @@ class AttachmentController extends Controller
      */
     public function getUpdateForm(string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        $attachment = Attachment::query()->findOrFail($attachmentId);
 
         $this->checkOwnablePermission('page-update', $attachment->page);
         $this->checkOwnablePermission('attachment-create', $attachment);
@@ -104,8 +102,8 @@ class AttachmentController extends Controller
      */
     public function update(Request $request, string $attachmentId)
     {
-        $attachment = $this->attachment->newQuery()->findOrFail($attachmentId);
-
+        /** @var Attachment $attachment */
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         try {
             $this->validate($request, [
                 'attachment_edit_name' => 'required|string|min:1|max:255',
@@ -160,7 +158,7 @@ class AttachmentController extends Controller
 
         $attachmentName = $request->get('attachment_link_name');
         $link = $request->get('attachment_link_url');
-        $attachment = $this->attachmentService->saveNewFromLink($attachmentName, $link, intval($pageId));
+        $this->attachmentService->saveNewFromLink($attachmentName, $link, intval($pageId));
 
         return view('attachments.manager-link-form', [
             'pageId' => $pageId,
@@ -202,9 +200,10 @@ class AttachmentController extends Controller
      * @throws FileNotFoundException
      * @throws NotFoundException
      */
-    public function get(string $attachmentId)
+    public function get(Request $request, string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        /** @var Attachment $attachment */
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         try {
             $page = $this->pageRepo->getById($attachment->uploaded_to);
         } catch (NotFoundException $exception) {
@@ -217,8 +216,13 @@ class AttachmentController extends Controller
             return redirect($attachment->path);
         }
 
+        $fileName = $attachment->getFileName();
         $attachmentContents = $this->attachmentService->getAttachmentFromStorage($attachment);
-        return $this->downloadResponse($attachmentContents, $attachment->getFileName());
+
+        if ($request->get('open') === 'true') {
+            return $this->inlineDownloadResponse($attachmentContents, $fileName);
+        }
+        return $this->downloadResponse($attachmentContents, $fileName);
     }
 
     /**
@@ -227,7 +231,8 @@ class AttachmentController extends Controller
      */
     public function delete(string $attachmentId)
     {
-        $attachment = $this->attachment->findOrFail($attachmentId);
+        /** @var Attachment $attachment */
+        $attachment = Attachment::query()->findOrFail($attachmentId);
         $this->checkOwnablePermission('attachment-delete', $attachment);
         $this->attachmentService->deleteFile($attachment);
         return response()->json(['message' => trans('entities.attachments_deleted')]);
index 01255f466886005e26c155b7217133bbc6f76825..16f7fc010adeedfe754269d93492209894d85706 100644 (file)
@@ -12,6 +12,7 @@ use BookStack\Http\Controllers\Controller;
 use BookStack\Theming\ThemeEvents;
 use Illuminate\Foundation\Auth\AuthenticatesUsers;
 use Illuminate\Http\Request;
+use Illuminate\Validation\ValidationException;
 
 class LoginController extends Controller
 {
@@ -198,4 +199,19 @@ class LoginController extends Controller
 
         return redirect('/login');
     }
+
+    /**
+     * Get the failed login response instance.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Symfony\Component\HttpFoundation\Response
+     *
+     * @throws \Illuminate\Validation\ValidationException
+     */
+    protected function sendFailedLoginResponse(Request $request)
+    {
+        throw ValidationException::withMessages([
+            $this->username() => [trans('auth.failed')],
+        ])->redirectTo('/login');
+    }
 }
index 59c205d0a9241770e6fd84c8d09e0f5f196cfb3b..64ae982d565a35707cbdef1d0e417366cc7df2f5 100644 (file)
@@ -2,6 +2,7 @@
 
 use Activity;
 use BookStack\Actions\ActivityType;
+use BookStack\Actions\View;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Models\Bookshelf;
 use BookStack\Entities\Tools\PermissionsUpdater;
@@ -11,7 +12,6 @@ use BookStack\Exceptions\ImageUploadException;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
-use Views;
 
 class BookController extends Controller
 {
@@ -112,7 +112,7 @@ class BookController extends Controller
         $bookChildren = (new BookContents($book))->getTree(true);
         $bookParentShelves = $book->shelves()->visible()->get();
 
-        Views::add($book);
+        View::incrementFor($book);
         if ($request->has('shelf')) {
             $this->entityContextManager->setShelfContext(intval($request->get('shelf')));
         }
index 1c1f124422f962020d31e4f35595b2e65d0f80cf..6d334ca737d62a3079d0695ee79a2601f252d923 100644 (file)
@@ -52,4 +52,14 @@ class BookExportController extends Controller
         $textContent = $this->exportFormatter->bookToPlainText($book);
         return $this->downloadResponse($textContent, $bookSlug . '.txt');
     }
+
+    /**
+     * Export a book as a markdown file.
+     */
+    public function markdown(string $bookSlug)
+    {
+        $book = $this->bookRepo->getBySlug($bookSlug);
+        $textContent = $this->exportFormatter->bookToMarkdown($book);
+        return $this->downloadResponse($textContent, $bookSlug . '.md');
+    }
 }
index 03b3cad54cc0a538fd8de9794e3d647b2b4dca0b..b4795db09029d3c6ea2d6a8136c98c8ee66bb039 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Http\Controllers;
 
 use Activity;
+use BookStack\Actions\View;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Entities\Tools\ShelfContext;
@@ -109,7 +110,7 @@ class BookshelfController extends Controller
             ->values()
             ->all();
 
-        Views::add($shelf);
+        View::incrementFor($shelf);
         $this->entityContextManager->setShelfContext($shelf->id);
         $view = setting()->getForCurrentUser('bookshelf_view_type');
 
index 1d69df2a2f6029e148ff59a4f1bb59f04678d8b0..d65b43cc12e701782628e838353f44dfc0fb6cb2 100644 (file)
@@ -1,15 +1,16 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\View;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Tools\BookContents;
 use BookStack\Entities\Repos\ChapterRepo;
+use BookStack\Entities\Tools\NextPreviousContentLocator;
 use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\MoveOperationException;
 use BookStack\Exceptions\NotFoundException;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
-use Views;
 
 class ChapterController extends Controller
 {
@@ -64,7 +65,8 @@ class ChapterController extends Controller
 
         $sidebarTree = (new BookContents($chapter->book))->getTree();
         $pages = $chapter->getVisiblePages();
-        Views::add($chapter);
+        $nextPreviousLocator = new NextPreviousContentLocator($chapter, $sidebarTree);
+        View::incrementFor($chapter);
 
         $this->setPageTitle($chapter->getShortName());
         return view('chapters.show', [
@@ -72,7 +74,9 @@ class ChapterController extends Controller
             'chapter' => $chapter,
             'current' => $chapter,
             'sidebarTree' => $sidebarTree,
-            'pages' => $pages
+            'pages' => $pages,
+            'next' => $nextPreviousLocator->getNext(),
+            'previous' => $nextPreviousLocator->getPrevious(),
         ]);
     }
 
index 52d087442ab287eb2d533365ed6cfd8cce5da642..b934eefce616f68269449c418f2574d7381b4b64 100644 (file)
@@ -54,4 +54,16 @@ class ChapterExportController extends Controller
         $chapterText = $this->exportFormatter->chapterToPlainText($chapter);
         return $this->downloadResponse($chapterText, $chapterSlug . '.txt');
     }
+
+    /**
+     * Export a chapter to a simple markdown file.
+     * @throws NotFoundException
+     */
+    public function markdown(string $bookSlug, string $chapterSlug)
+    {
+        // TODO: This should probably export to a zip file.
+        $chapter = $this->chapterRepo->getBySlug($bookSlug, $chapterSlug);
+        $chapterText = $this->exportFormatter->chapterToMarkdown($chapter);
+        return $this->downloadResponse($chapterText, $chapterSlug . '.md');
+    }
 }
index 034dfa524192013b4766880ebb878176ef95d496..47b03b28d48aaa29610ef562635670af6adbb630 100644 (file)
@@ -6,6 +6,7 @@ use BookStack\Facades\Activity;
 use BookStack\Interfaces\Loggable;
 use BookStack\HasCreatorAndUpdater;
 use BookStack\Model;
+use finfo;
 use Illuminate\Foundation\Bus\DispatchesJobs;
 use Illuminate\Foundation\Validation\ValidatesRequests;
 use Illuminate\Http\Exceptions\HttpResponseException;
@@ -121,6 +122,20 @@ abstract class Controller extends BaseController
         ]);
     }
 
+    /**
+     * Create a file download response that provides the file with a content-type
+     * correct for the file, in a way so the browser can show the content in browser.
+     */
+    protected function inlineDownloadResponse(string $content, string $fileName): Response
+    {
+        $finfo = new finfo(FILEINFO_MIME_TYPE);
+        $mime = $finfo->buffer($content) ?: 'application/octet-stream';
+        return response()->make($content, 200, [
+            'Content-Type'        => $mime,
+            'Content-Disposition' => 'inline; filename="' . $fileName . '"'
+        ]);
+    }
+
     /**
      * Show a positive, successful notification to the user on next view load.
      */
diff --git a/app/Http/Controllers/FavouriteController.php b/app/Http/Controllers/FavouriteController.php
new file mode 100644 (file)
index 0000000..f4aeb4f
--- /dev/null
@@ -0,0 +1,95 @@
+<?php
+
+namespace BookStack\Http\Controllers;
+
+use BookStack\Entities\Models\Entity;
+use BookStack\Entities\Queries\TopFavourites;
+use BookStack\Interfaces\Favouritable;
+use BookStack\Model;
+use Illuminate\Http\Request;
+
+class FavouriteController extends Controller
+{
+    /**
+     * Show a listing of all favourite items for the current user.
+     */
+    public function index(Request $request)
+    {
+        $viewCount = 20;
+        $page = intval($request->get('page', 1));
+        $favourites = (new TopFavourites)->run($viewCount + 1, (($page - 1) * $viewCount));
+
+        $hasMoreLink = ($favourites->count() > $viewCount) ? url("/favourites?page=" . ($page+1)) : null;
+
+        return view('common.detailed-listing-with-more', [
+            'title' => trans('entities.my_favourites'),
+            'entities' => $favourites->slice(0, $viewCount),
+            'hasMoreLink' => $hasMoreLink,
+        ]);
+    }
+
+    /**
+     * Add a new item as a favourite.
+     */
+    public function add(Request $request)
+    {
+        $favouritable = $this->getValidatedModelFromRequest($request);
+        $favouritable->favourites()->firstOrCreate([
+            'user_id' => user()->id,
+        ]);
+
+        $this->showSuccessNotification(trans('activities.favourite_add_notification', [
+            'name' => $favouritable->name,
+        ]));
+        return redirect()->back();
+    }
+
+    /**
+     * Remove an item as a favourite.
+     */
+    public function remove(Request $request)
+    {
+        $favouritable = $this->getValidatedModelFromRequest($request);
+        $favouritable->favourites()->where([
+            'user_id' => user()->id,
+        ])->delete();
+
+        $this->showSuccessNotification(trans('activities.favourite_remove_notification', [
+            'name' => $favouritable->name,
+        ]));
+        return redirect()->back();
+    }
+
+    /**
+     * @throws \Illuminate\Validation\ValidationException
+     * @throws \Exception
+     */
+    protected function getValidatedModelFromRequest(Request $request): Favouritable
+    {
+        $modelInfo = $this->validate($request, [
+            'type' => 'required|string',
+            'id' => 'required|integer',
+        ]);
+
+        if (!class_exists($modelInfo['type'])) {
+            throw new \Exception('Model not found');
+        }
+
+        /** @var Model $model */
+        $model = new $modelInfo['type'];
+        if (! $model instanceof Favouritable) {
+            throw new \Exception('Model not favouritable');
+        }
+
+        $modelInstance = $model->newQuery()
+            ->where('id', '=', $modelInfo['id'])
+            ->first(['id', 'name']);
+
+        $inaccessibleEntity = ($modelInstance instanceof Entity && !userCan('view', $modelInstance));
+        if (is_null($modelInstance) || $inaccessibleEntity) {
+            throw new \Exception('Model instance not found');
+        }
+
+        return $modelInstance;
+    }
+}
index 31736e1b09ab92bcb778c9d61dd928e3dbfa884a..7bc17052652aa65652b762c9335aee9fd72eaf4f 100644 (file)
@@ -2,11 +2,12 @@
 
 use Activity;
 use BookStack\Entities\Models\Book;
+use BookStack\Entities\Queries\RecentlyViewed;
+use BookStack\Entities\Queries\TopFavourites;
 use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\BookRepo;
 use BookStack\Entities\Repos\BookshelfRepo;
-use Illuminate\Http\Response;
 use Views;
 
 class HomeController extends Controller
@@ -32,12 +33,13 @@ class HomeController extends Controller
 
         $recentFactor = count($draftPages) > 0 ? 0.5 : 1;
         $recents = $this->isSignedIn() ?
-              Views::getUserRecentlyViewed(12*$recentFactor, 1)
+            (new RecentlyViewed)->run(12*$recentFactor, 1)
             : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get();
+        $favourites = (new TopFavourites)->run(6);
         $recentlyUpdatedPages = Page::visible()->with('book')
             ->where('draft', false)
             ->orderBy('updated_at', 'desc')
-            ->take(12)
+            ->take($favourites->count() > 0 ? 6 : 12)
             ->get();
 
         $homepageOptions = ['default', 'books', 'bookshelves', 'page'];
@@ -51,6 +53,7 @@ class HomeController extends Controller
             'recents' => $recents,
             'recentlyUpdatedPages' => $recentlyUpdatedPages,
             'draftPages' => $draftPages,
+            'favourites' => $favourites,
         ];
 
         // Add required list ordering & sorting for books & shelves views.
@@ -105,7 +108,7 @@ class HomeController extends Controller
      */
     public function customHeadContent()
     {
-        return view('partials.custom-head-content');
+        return view('partials.custom-head');
     }
 
     /**
index ecc36bf67e24ad531f83326ed32d22bf4f97f63d..1eb8917b360edb2fb90ddaff26eff1e2028d137a 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace BookStack\Http\Controllers\Images;
 
 use BookStack\Exceptions\ImageUploadException;
+use BookStack\Exceptions\NotFoundException;
 use BookStack\Http\Controllers\Controller;
 use BookStack\Uploads\Image;
 use BookStack\Uploads\ImageRepo;
@@ -27,12 +28,15 @@ class ImageController extends Controller
 
     /**
      * Provide an image file from storage.
+     * @throws NotFoundException
      */
     public function showImage(string $path)
     {
         $path = storage_path('uploads/images/' . $path);
         if (!file_exists($path)) {
-            abort(404);
+            throw (new NotFoundException(trans('errors.image_not_found')))
+                ->setSubtitle(trans('errors.image_not_found_subtitle'))
+                ->setDetails(trans('errors.image_not_found_details'));
         }
 
         return response()->file($path);
index 7d8e54382961006db647b5f1b1b4fe9982337a2d..31ee4e970bd4952c9a9613863a723d33f7972560 100644 (file)
@@ -1,19 +1,19 @@
 <?php namespace BookStack\Http\Controllers;
 
+use BookStack\Actions\View;
 use BookStack\Entities\Tools\BookContents;
+use BookStack\Entities\Tools\NextPreviousContentLocator;
 use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Tools\PageEditActivity;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Entities\Tools\PermissionsUpdater;
 use BookStack\Exceptions\NotFoundException;
-use BookStack\Exceptions\NotifyException;
 use BookStack\Exceptions\PermissionsException;
 use Exception;
 use Illuminate\Http\Request;
 use Illuminate\Validation\ValidationException;
 use Throwable;
-use Views;
 
 class PageController extends Controller
 {
@@ -142,7 +142,9 @@ class PageController extends Controller
             $page->load(['comments.createdBy']);
         }
 
-        Views::add($page);
+        $nextPreviousLocator = new NextPreviousContentLocator($page, $sidebarTree);
+
+        View::incrementFor($page);
         $this->setPageTitle($page->getShortName());
         return view('pages.show', [
             'page' => $page,
@@ -150,7 +152,9 @@ class PageController extends Controller
             'current' => $page,
             'sidebarTree' => $sidebarTree,
             'commentsEnabled' => $commentsEnabled,
-            'pageNav' => $pageNav
+            'pageNav' => $pageNav,
+            'next' => $nextPreviousLocator->getNext(),
+            'previous' => $nextPreviousLocator->getPrevious(),
         ]);
     }
 
@@ -243,8 +247,8 @@ class PageController extends Controller
 
         $updateTime = $draft->updated_at->timestamp;
         return response()->json([
-            'status'    => 'success',
-            'message'   => trans('entities.pages_edit_draft_save_at'),
+            'status' => 'success',
+            'message' => trans('entities.pages_edit_draft_save_at'),
             'timestamp' => $updateTime
         ]);
     }
@@ -267,7 +271,7 @@ class PageController extends Controller
     {
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('page-delete', $page);
-        $this->setPageTitle(trans('entities.pages_delete_named', ['pageName'=>$page->getShortName()]));
+        $this->setPageTitle(trans('entities.pages_delete_named', ['pageName' => $page->getShortName()]));
         return view('pages.delete', [
             'book' => $page->book,
             'page' => $page,
@@ -283,7 +287,7 @@ class PageController extends Controller
     {
         $page = $this->pageRepo->getById($pageId);
         $this->checkOwnablePermission('page-update', $page);
-        $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName'=>$page->getShortName()]));
+        $this->setPageTitle(trans('entities.pages_delete_draft_named', ['pageName' => $page->getShortName()]));
         return view('pages.delete', [
             'book' => $page->book,
             'page' => $page,
@@ -295,7 +299,6 @@ class PageController extends Controller
      * Remove the specified page from storage.
      * @throws NotFoundException
      * @throws Throwable
-     * @throws NotifyException
      */
     public function destroy(string $bookSlug, string $pageSlug)
     {
@@ -311,7 +314,6 @@ class PageController extends Controller
     /**
      * Remove the specified draft page from storage.
      * @throws NotFoundException
-     * @throws NotifyException
      * @throws Throwable
      */
     public function destroyDraft(string $bookSlug, int $pageId)
@@ -340,9 +342,9 @@ class PageController extends Controller
             ->paginate(20)
             ->setPath(url('/pages/recently-updated'));
 
-        return view('pages.detailed-listing', [
+        return view('common.detailed-listing-paginated', [
             'title' => trans('entities.recently_updated_pages'),
-            'pages' => $pages
+            'entities' => $pages
         ]);
     }
 
@@ -380,7 +382,7 @@ class PageController extends Controller
         try {
             $parent = $this->pageRepo->move($page, $entitySelection);
         } catch (Exception $exception) {
-            if ($exception instanceof  PermissionsException) {
+            if ($exception instanceof PermissionsException) {
                 $this->showPermissionError();
             }
 
@@ -424,7 +426,7 @@ class PageController extends Controller
         try {
             $pageCopy = $this->pageRepo->copy($page, $entitySelection, $newName);
         } catch (Exception $exception) {
-            if ($exception instanceof  PermissionsException) {
+            if ($exception instanceof PermissionsException) {
                 $this->showPermissionError();
             }
 
@@ -445,7 +447,7 @@ class PageController extends Controller
         $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
         $this->checkOwnablePermission('restrictions-manage', $page);
         return view('pages.permissions', [
-            'page'  => $page,
+            'page' => $page,
         ]);
     }
 
index e5e027fe72cd2f5cec19418d9ea81901238e2eb7..f5dcf7117ae823f2d14e502bc39557cd02b09dd4 100644 (file)
@@ -60,4 +60,15 @@ class PageExportController extends Controller
         $pageText = $this->exportFormatter->pageToPlainText($page);
         return $this->downloadResponse($pageText, $pageSlug . '.txt');
     }
+
+    /**
+     * Export a page to a simple markdown .md file.
+     * @throws NotFoundException
+     */
+    public function markdown(string $bookSlug, string $pageSlug)
+    {
+        $page = $this->pageRepo->getBySlug($bookSlug, $pageSlug);
+        $pageText = $this->exportFormatter->pageToMarkdown($page);
+        return $this->downloadResponse($pageText, $pageSlug . '.md');
+    }
 }
index bb824fd9bfedf1dec20799df7ab25d0b536774e3..8598575004f04a5e1c679288e6f5d7060b57a54a 100644 (file)
@@ -1,6 +1,6 @@
 <?php namespace BookStack\Http\Controllers;
 
-use BookStack\Actions\ViewService;
+use BookStack\Entities\Queries\Popular;
 use BookStack\Entities\Tools\SearchRunner;
 use BookStack\Entities\Tools\ShelfContext;
 use BookStack\Entities\Tools\SearchOptions;
@@ -9,16 +9,13 @@ use Illuminate\Http\Request;
 
 class SearchController extends Controller
 {
-    protected $viewService;
     protected $searchRunner;
     protected $entityContextManager;
 
     public function __construct(
-        ViewService $viewService,
         SearchRunner $searchRunner,
         ShelfContext $entityContextManager
     ) {
-        $this->viewService = $viewService;
         $this->searchRunner = $searchRunner;
         $this->entityContextManager = $entityContextManager;
     }
@@ -82,7 +79,7 @@ class SearchController extends Controller
             $searchTerm .= ' {type:'. implode('|', $entityTypes) .'}';
             $entities = $this->searchRunner->searchEntities(SearchOptions::fromString($searchTerm), 'all', 1, 20, $permission)['results'];
         } else {
-            $entities = $this->viewService->getPopular(20, 0, $entityTypes, $permission);
+            $entities = (new Popular)->run(20, 0, $entityTypes, $permission);
         }
 
         return view('search.entity-ajax-list', ['entities' => $entities]);
index cd084746c5ca2b6ea432d5d2ab863e807bc94581..0b286a713f8398f45811b78e82c0fac990991185 100644 (file)
@@ -28,6 +28,7 @@ class Localization
         'es_AR' => 'es_AR',
         'fr' => 'fr_FR',
         'he' => 'he_IL',
+        'hr' => 'hr_HR',
         'id' => 'id_ID',
         'it' => 'it_IT',
         'ja' => 'ja',
diff --git a/app/Interfaces/Favouritable.php b/app/Interfaces/Favouritable.php
new file mode 100644 (file)
index 0000000..dd335fe
--- /dev/null
@@ -0,0 +1,11 @@
+<?php namespace BookStack\Interfaces;
+
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+
+interface Favouritable
+{
+    /**
+     * Get the related favourite instances.
+     */
+    public function favourites(): MorphMany;
+}
\ No newline at end of file
diff --git a/app/Interfaces/Viewable.php b/app/Interfaces/Viewable.php
new file mode 100644 (file)
index 0000000..4a7b6a0
--- /dev/null
@@ -0,0 +1,11 @@
+<?php namespace BookStack\Interfaces;
+
+use Illuminate\Database\Eloquent\Relations\MorphMany;
+
+interface Viewable
+{
+    /**
+     * Get all view instances for this viewable model.
+     */
+    public function views(): MorphMany;
+}
\ No newline at end of file
index f203f0fda592f5e27e7099b851395207bf8de71b..ca86b6607e2d2875ad006f884821616c180f6201 100644 (file)
@@ -3,7 +3,6 @@
 namespace BookStack\Providers;
 
 use BookStack\Actions\ActivityService;
-use BookStack\Actions\ViewService;
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Theming\ThemeService;
 use BookStack\Uploads\ImageService;
@@ -32,10 +31,6 @@ class CustomFacadeProvider extends ServiceProvider
             return $this->app->make(ActivityService::class);
         });
 
-        $this->app->singleton('views', function () {
-            return $this->app->make(ViewService::class);
-        });
-
         $this->app->singleton('images', function () {
             return $this->app->make(ImageService::class);
         });
index 54e476ae2a896b65abaeafa2689406659affa1a1..895108e3e2aac8ee5791c266bd30c9ba6c1e951e 100644 (file)
@@ -53,9 +53,9 @@ class ThemeService
     /**
      * @see SocialAuthService::addSocialDriver
      */
-    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler)
+    public function addSocialDriver(string $driverName, array $config, string $socialiteHandler, callable $configureForRedirect = null)
     {
         $socialAuthService = app()->make(SocialAuthService::class);
-        $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler);
+        $socialAuthService->addSocialDriver($driverName, $config, $socialiteHandler, $configureForRedirect);
     }
 }
\ No newline at end of file
index d1060477d085d3cda5c23b7363c5d54067de7b3d..383af9537d34a20590e1609bcaddeaa8a9b11a67 100644 (file)
@@ -3,12 +3,14 @@
 use BookStack\Entities\Models\Page;
 use BookStack\Model;
 use BookStack\Traits\HasCreatorAndUpdater;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
 /**
  * @property int id
  * @property string name
  * @property string path
  * @property string extension
+ * @property ?Page page
  * @property bool external
  */
 class Attachment extends Model
@@ -31,9 +33,8 @@ class Attachment extends Model
 
     /**
      * Get the page this file was uploaded to.
-     * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
      */
-    public function page()
+    public function page(): BelongsTo
     {
         return $this->belongsTo(Page::class, 'uploaded_to');
     }
@@ -41,12 +42,12 @@ class Attachment extends Model
     /**
      * Get the url of this file.
      */
-    public function getUrl(): string
+    public function getUrl($openInline = false): string
     {
         if ($this->external && strpos($this->path, 'http') !== 0) {
             return $this->path;
         }
-        return url('/attachments/' . $this->id);
+        return url('/attachments/' . $this->id . ($openInline ? '?open=true' : ''));
     }
 
     /**
index b14f49473709647e23fd03afeea406d39c752361..37adb4f8368f0c355fcad8ae3bfd37037876c402 100644 (file)
@@ -3,8 +3,10 @@
 use BookStack\Exceptions\FileUploadException;
 use Exception;
 use Illuminate\Contracts\Filesystem\Factory as FileSystem;
+use Illuminate\Contracts\Filesystem\FileNotFoundException;
 use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
 use Illuminate\Support\Str;
+use Log;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
 class AttachmentService
@@ -38,11 +40,9 @@ class AttachmentService
 
     /**
      * Get an attachment from storage.
-     * @param Attachment $attachment
-     * @return string
-     * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
+     * @throws FileNotFoundException
      */
-    public function getAttachmentFromStorage(Attachment $attachment)
+    public function getAttachmentFromStorage(Attachment $attachment): string
     {
         return $this->getStorage()->get($attachment->path);
     }
@@ -202,6 +202,7 @@ class AttachmentService
         try {
             $storage->put($attachmentPath, $attachmentData);
         } catch (Exception $e) {
+            Log::error('Error when attempting file upload:' . $e->getMessage());
             throw new FileUploadException(trans('errors.path_not_writable', ['filePath' => $attachmentPath]));
         }
 
index dc26af002ab5e29de70d3679d9a56282b39bb458..3657aa9469d2bac249fe0b146608a4784ba96eef 100644 (file)
@@ -3,8 +3,17 @@
 use BookStack\Entities\Models\Page;
 use BookStack\Model;
 use BookStack\Traits\HasCreatorAndUpdater;
-use Images;
 
+/**
+ * @property int $id
+ * @property string $name
+ * @property string $url
+ * @property string $path
+ * @property string $type
+ * @property int $uploaded_to
+ * @property int $created_by
+ * @property int $updated_by
+ */
 class Image extends Model
 {
     use HasCreatorAndUpdater;
@@ -14,23 +23,18 @@ class Image extends Model
 
     /**
      * Get a thumbnail for this image.
-     * @param  int $width
-     * @param  int $height
-     * @param bool|false $keepRatio
-     * @return string
      * @throws \Exception
      */
-    public function getThumb($width, $height, $keepRatio = false)
+    public function getThumb(int $width, int $height, bool $keepRatio = false): string
     {
-        return Images::getThumbnail($this, $width, $height, $keepRatio);
+        return app()->make(ImageService::class)->getThumbnail($this, $width, $height, $keepRatio);
     }
 
     /**
      * Get the page this image has been uploaded to.
      * Only applicable to gallery or drawio image types.
-     * @return Page|null
      */
-    public function getPage()
+    public function getPage(): ?Page
     {
         return $this->belongsTo(Page::class, 'uploaded_to')->first();
     }
index e6f7668241dafa4dcfad7a0b852bfc6d214904c8..ef249c58b02b59710133d25e71d871b4ff255b9c 100644 (file)
@@ -130,6 +130,17 @@ class ImageRepo
         return $image;
     }
 
+    /**
+     * Save a new image from an existing image data string.
+     * @throws ImageUploadException
+     */
+    public function saveNewFromData(string $imageName, string $imageData, string $type, int $uploadedTo = 0)
+    {
+        $image = $this->imageService->saveNew($imageName, $imageData, $type, $uploadedTo);
+        $this->loadThumbs($image);
+        return $image;
+    }
+
     /**
      * Save a drawing the the database.
      * @throws ImageUploadException
index 02fe2e8ef35c48a640411958e124426d629803c2..293049f4f8c35e0b55edd76c772d425386079703 100644 (file)
@@ -8,6 +8,7 @@ use Illuminate\Contracts\Cache\Repository as Cache;
 use Illuminate\Contracts\Filesystem\Factory as FileSystem;
 use Illuminate\Contracts\Filesystem\Filesystem as FileSystemInstance;
 use Illuminate\Contracts\Filesystem\FileNotFoundException;
+use Illuminate\Contracts\Filesystem\Filesystem as Storage;
 use Illuminate\Support\Str;
 use Intervention\Image\Exception\NotSupportedException;
 use Intervention\Image\ImageManager;
@@ -106,9 +107,9 @@ class ImageService
         }
 
         try {
-            $storage->put($fullPath, $imageData);
-            $storage->setVisibility($fullPath, 'public');
+            $this->saveImageDataInPublicSpace($storage, $fullPath, $imageData);
         } catch (Exception $e) {
+            \Log::error('Error when attempting image upload:' . $e->getMessage());
             throw new ImageUploadException(trans('errors.path_not_writable', ['filePath' => $fullPath]));
         }
 
@@ -131,6 +132,25 @@ class ImageService
         return $image;
     }
 
+    /**
+     * Save image data for the given path in the public space, if possible,
+     * for the provided storage mechanism.
+     */
+    protected function saveImageDataInPublicSpace(Storage $storage, string $path, string $data)
+    {
+        $storage->put($path, $data);
+
+        // Set visibility when a non-AWS-s3, s3-like storage option is in use.
+        // Done since this call can break s3-like services but desired for other image stores.
+        // Attempting to set ACL during above put request requires different permissions
+        // hence would technically be a breaking change for actual s3 usage.
+        $usingS3 = strtolower(config('filesystems.images')) === 's3';
+        $usingS3Like = $usingS3 && !is_null(config('filesystems.disks.s3.endpoint'));
+        if (!$usingS3Like) {
+            $storage->setVisibility($path, 'public');
+        }
+    }
+
     /**
      * Clean up an image file name to be both URL and storage safe.
      */
@@ -190,8 +210,7 @@ class ImageService
 
         $thumbData = $this->resizeImage($storage->get($imagePath), $width, $height, $keepRatio);
 
-        $storage->put($thumbFilePath, $thumbData);
-        $storage->setVisibility($thumbFilePath, 'public');
+        $this->saveImageDataInPublicSpace($storage, $thumbFilePath, $thumbData);
         $this->cache->put('images-' . $image->id . '-' . $thumbFilePath, $thumbFilePath, 60 * 60 * 72);
 
 
index f1509bbb8079b163f9ce15b035adf430622a8fc9..e98c1cfcaf5fa19488415f9237b00d4b46d12afc 100644 (file)
@@ -26,6 +26,7 @@ class UserAvatars
         }
 
         try {
+            $this->destroyAllForUser($user);
             $avatar = $this->saveAvatarImage($user);
             $user->avatar()->associate($avatar);
             $user->save();
@@ -34,6 +35,35 @@ class UserAvatars
         }
     }
 
+    /**
+     * Assign a new avatar image to the given user using the given image data.
+     */
+    public function assignToUserFromExistingData(User $user, string $imageData, string $extension): void
+    {
+        try {
+            $this->destroyAllForUser($user);
+            $avatar = $this->createAvatarImageFromData($user, $imageData, $extension);
+            $user->avatar()->associate($avatar);
+            $user->save();
+        } catch (Exception $e) {
+            Log::error('Failed to save user avatar image');
+        }
+    }
+
+    /**
+     * Destroy all user avatars uploaded to the given user.
+     */
+    public function destroyAllForUser(User $user)
+    {
+        $profileImages = Image::query()->where('type', '=', 'user')
+            ->where('uploaded_to', '=', $user->id)
+            ->get();
+
+        foreach ($profileImages as $image) {
+            $this->imageService->destroy($image);
+        }
+    }
+
     /**
      * Save an avatar image from an external service.
      * @throws Exception
@@ -50,8 +80,16 @@ class UserAvatars
         ];
 
         $userAvatarUrl = strtr($avatarUrl, $replacements);
-        $imageName = str_replace(' ', '-', $user->id . '-avatar.png');
         $imageData = $this->getAvatarImageData($userAvatarUrl);
+        return $this->createAvatarImageFromData($user, $imageData, 'png');
+    }
+
+    /**
+     * Creates a new image instance and saves it in the system as a new user avatar image.
+     */
+    protected function createAvatarImageFromData(User $user, string $imageData, string $extension): Image
+    {
+        $imageName = str_replace(' ', '-', $user->id . '-avatar.' . $extension);
 
         $image = $this->imageService->saveNew($imageName, $imageData, 'user', $user->id);
         $image->created_by = $user->id;
diff --git a/app/Util/HtmlContentFilter.php b/app/Util/HtmlContentFilter.php
new file mode 100644 (file)
index 0000000..11be5a0
--- /dev/null
@@ -0,0 +1,70 @@
+<?php namespace BookStack\Util;
+
+use DOMDocument;
+use DOMNodeList;
+use DOMXPath;
+
+class HtmlContentFilter
+{
+    /**
+     * Remove all of the script elements from the given HTML.
+     */
+    public static function removeScripts(string $html): string
+    {
+        if (empty($html)) {
+            return $html;
+        }
+
+        $html = '<body>' . $html . '</body>';
+        libxml_use_internal_errors(true);
+        $doc = new DOMDocument();
+        $doc->loadHTML(mb_convert_encoding($html, 'HTML-ENTITIES', 'UTF-8'));
+        $xPath = new DOMXPath($doc);
+
+        // Remove standard script tags
+        $scriptElems = $xPath->query('//script');
+        static::removeNodes($scriptElems);
+
+        // Remove clickable links to JavaScript URI
+        $badLinks = $xPath->query('//*[contains(@href, \'javascript:\')]');
+        static::removeNodes($badLinks);
+
+        // Remove forms with calls to JavaScript URI
+        $badForms = $xPath->query('//*[contains(@action, \'javascript:\')] | //*[contains(@formaction, \'javascript:\')]');
+        static::removeNodes($badForms);
+
+        // Remove meta tag to prevent external redirects
+        $metaTags = $xPath->query('//meta[contains(@content, \'url\')]');
+        static::removeNodes($metaTags);
+
+        // Remove data or JavaScript iFrames
+        $badIframes = $xPath->query('//*[contains(@src, \'data:\')] | //*[contains(@src, \'javascript:\')] | //*[@srcdoc]');
+        static::removeNodes($badIframes);
+
+        // Remove 'on*' attributes
+        $onAttributes = $xPath->query('//@*[starts-with(name(), \'on\')]');
+        foreach ($onAttributes as $attr) {
+            /** @var \DOMAttr $attr*/
+            $attrName = $attr->nodeName;
+            $attr->parentNode->removeAttribute($attrName);
+        }
+
+        $html = '';
+        $topElems = $doc->documentElement->childNodes->item(0)->childNodes;
+        foreach ($topElems as $child) {
+            $html .= $doc->saveHTML($child);
+        }
+
+        return $html;
+    }
+
+    /**
+     * Removed all of the given DOMNodes.
+     */
+    protected static function removeNodes(DOMNodeList $nodes): void
+    {
+        foreach ($nodes as $node) {
+            $node->parentNode->removeChild($node);
+        }
+    }
+}
index 3e604b8fdfb29dd13cef6be681971ecdad0a9a9b..4125ae7fe01cf30a374921e46b4a943d374a7b55 100644 (file)
@@ -8,6 +8,7 @@
         "php": "^7.3|^8.0",
         "ext-curl": "*",
         "ext-dom": "*",
+        "ext-fileinfo": "*",
         "ext-gd": "*",
         "ext-json": "*",
         "ext-mbstring": "*",
@@ -22,6 +23,7 @@
         "laravel/socialite": "^5.1",
         "league/commonmark": "^1.5",
         "league/flysystem-aws-s3-v3": "^1.0.29",
+        "league/html-to-markdown": "^5.0.0",
         "nunomaduro/collision": "^3.1",
         "onelogin/php-saml": "^4.0",
         "predis/predis": "^1.1.6",
index 0a14f0d77d383150c3f9330fb4af5532de08ce0e..a7868b42afa48eecf4c6108b776232a21efd418a 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": "b26d29958d84c91b164a8234d1a7e9e9",
+    "content-hash": "70a290f0e2112361af4a0abe15148adc",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.175.0",
+            "version": "3.184.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "31baa9e0c4330f01eb74b8a7ef086e9d34f8391e"
+                "reference": "d5d5fe5fdfca6c7a56f2f8364d09c3100d5c2149"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/31baa9e0c4330f01eb74b8a7ef086e9d34f8391e",
-                "reference": "31baa9e0c4330f01eb74b8a7ef086e9d34f8391e",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d5d5fe5fdfca6c7a56f2f8364d09c3100d5c2149",
+                "reference": "d5d5fe5fdfca6c7a56f2f8364d09c3100d5c2149",
                 "shasum": ""
             },
             "require": {
@@ -92,9 +92,9 @@
             "support": {
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.175.0"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.184.7"
             },
-            "time": "2021-03-19T18:13:22+00:00"
+            "time": "2021-06-21T18:37:16+00:00"
         },
         {
             "name": "barryvdh/laravel-dompdf",
         },
         {
             "name": "doctrine/cache",
-            "version": "1.10.2",
+            "version": "2.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "13e3381b25847283a91948d04640543941309727"
+                "reference": "c9622c6820d3ede1e2315a6a377ea1076e421d88"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/13e3381b25847283a91948d04640543941309727",
-                "reference": "13e3381b25847283a91948d04640543941309727",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/c9622c6820d3ede1e2315a6a377ea1076e421d88",
+                "reference": "c9622c6820d3ede1e2315a6a377ea1076e421d88",
                 "shasum": ""
             },
             "require": {
                 "php": "~7.1 || ^8.0"
             },
             "conflict": {
-                "doctrine/common": ">2.2,<2.4"
+                "doctrine/common": ">2.2,<2.4",
+                "psr/cache": ">=3"
             },
             "require-dev": {
                 "alcaeus/mongo-php-adapter": "^1.1",
-                "doctrine/coding-standard": "^6.0",
+                "cache/integration-tests": "dev-master",
+                "doctrine/coding-standard": "^8.0",
                 "mongodb/mongodb": "^1.1",
-                "phpunit/phpunit": "^7.0",
-                "predis/predis": "~1.0"
+                "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0",
+                "predis/predis": "~1.0",
+                "psr/cache": "^1.0 || ^2.0",
+                "symfony/cache": "^4.4 || ^5.2"
             },
             "suggest": {
                 "alcaeus/mongo-php-adapter": "Required to use legacy MongoDB driver"
             },
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "1.9.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache"
             ],
             "support": {
                 "issues": "https://github.com/doctrine/cache/issues",
-                "source": "https://github.com/doctrine/cache/tree/1.10.x"
+                "source": "https://github.com/doctrine/cache/tree/2.0.3"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-07-07T18:54:01+00:00"
+            "time": "2021-05-25T09:43:04+00:00"
         },
         {
             "name": "doctrine/dbal",
-            "version": "2.12.1",
+            "version": "2.13.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "adce7a954a1c2f14f85e94aed90c8489af204086"
+                "reference": "8dd39d2ead4409ce652fd4f02621060f009ea5e4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/adce7a954a1c2f14f85e94aed90c8489af204086",
-                "reference": "adce7a954a1c2f14f85e94aed90c8489af204086",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/8dd39d2ead4409ce652fd4f02621060f009ea5e4",
+                "reference": "8dd39d2ead4409ce652fd4f02621060f009ea5e4",
                 "shasum": ""
             },
             "require": {
-                "doctrine/cache": "^1.0",
+                "doctrine/cache": "^1.0|^2.0",
+                "doctrine/deprecations": "^0.5.3",
                 "doctrine/event-manager": "^1.0",
                 "ext-pdo": "*",
-                "php": "^7.3 || ^8"
+                "php": "^7.1 || ^8"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^8.1",
-                "jetbrains/phpstorm-stubs": "^2019.1",
-                "phpstan/phpstan": "^0.12.40",
-                "phpunit/phpunit": "^9.4",
-                "psalm/plugin-phpunit": "^0.10.0",
+                "doctrine/coding-standard": "9.0.0",
+                "jetbrains/phpstorm-stubs": "2020.2",
+                "phpstan/phpstan": "0.12.81",
+                "phpunit/phpunit": "^7.5.20|^8.5|9.5.5",
+                "squizlabs/php_codesniffer": "3.6.0",
+                "symfony/cache": "^4.4",
                 "symfony/console": "^2.0.5|^3.0|^4.0|^5.0",
-                "vimeo/psalm": "^3.17.2"
+                "vimeo/psalm": "4.6.4"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
                 "bin/doctrine-dbal"
             ],
             "type": "library",
-            "extra": {
-                "branch-alias": {
-                    "dev-master": "4.0.x-dev"
-                }
-            },
             "autoload": {
                 "psr-4": {
                     "Doctrine\\DBAL\\": "lib/Doctrine/DBAL"
             ],
             "support": {
                 "issues": "https://github.com/doctrine/dbal/issues",
-                "source": "https://github.com/doctrine/dbal/tree/2.12.1"
+                "source": "https://github.com/doctrine/dbal/tree/2.13.2"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-11-14T20:26:58+00:00"
+            "time": "2021-06-18T21:48:39+00:00"
+        },
+        {
+            "name": "doctrine/deprecations",
+            "version": "v0.5.3",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/doctrine/deprecations.git",
+                "reference": "9504165960a1f83cc1480e2be1dd0a0478561314"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/doctrine/deprecations/zipball/9504165960a1f83cc1480e2be1dd0a0478561314",
+                "reference": "9504165960a1f83cc1480e2be1dd0a0478561314",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^7.1|^8.0"
+            },
+            "require-dev": {
+                "doctrine/coding-standard": "^6.0|^7.0|^8.0",
+                "phpunit/phpunit": "^7.0|^8.0|^9.0",
+                "psr/log": "^1.0"
+            },
+            "suggest": {
+                "psr/log": "Allows logging deprecations via PSR-3 logger implementation"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.",
+            "homepage": "https://www.doctrine-project.org/",
+            "support": {
+                "issues": "https://github.com/doctrine/deprecations/issues",
+                "source": "https://github.com/doctrine/deprecations/tree/v0.5.3"
+            },
+            "time": "2021-03-21T12:59:47+00:00"
         },
         {
             "name": "doctrine/event-manager",
         },
         {
             "name": "facade/flare-client-php",
-            "version": "1.4.0",
+            "version": "1.8.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/flare-client-php.git",
-                "reference": "ef0f5bce23b30b32d98fd9bb49c6fa37b40eb546"
+                "reference": "47b639dc02bcfdfc4ebb83de703856fa01e35f5f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/ef0f5bce23b30b32d98fd9bb49c6fa37b40eb546",
-                "reference": "ef0f5bce23b30b32d98fd9bb49c6fa37b40eb546",
+                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/47b639dc02bcfdfc4ebb83de703856fa01e35f5f",
+                "reference": "47b639dc02bcfdfc4ebb83de703856fa01e35f5f",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/facade/flare-client-php/issues",
-                "source": "https://github.com/facade/flare-client-php/tree/1.4.0"
+                "source": "https://github.com/facade/flare-client-php/tree/1.8.1"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2021-02-16T12:42:06+00:00"
+            "time": "2021-05-31T19:23:29+00:00"
         },
         {
             "name": "facade/ignition",
-            "version": "1.16.15",
+            "version": "1.17.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "b6aea4a99303d9d32afd486a285162a89af8a8a3"
+                "reference": "dc49305538aeb77e4c89eba57cee4ceff9e42f33"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/b6aea4a99303d9d32afd486a285162a89af8a8a3",
-                "reference": "b6aea4a99303d9d32afd486a285162a89af8a8a3",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/dc49305538aeb77e4c89eba57cee4ceff9e42f33",
+                "reference": "dc49305538aeb77e4c89eba57cee4ceff9e42f33",
                 "shasum": ""
             },
             "require": {
                 "issues": "https://github.com/facade/ignition/issues",
                 "source": "https://github.com/facade/ignition"
             },
-            "time": "2021-02-15T10:21:49+00:00"
+            "time": "2021-05-25T07:15:52+00:00"
         },
         {
             "name": "facade/ignition-contracts",
         },
         {
             "name": "filp/whoops",
-            "version": "2.11.0",
+            "version": "2.13.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/filp/whoops.git",
-                "reference": "f6e14679f948d8a5cfb866fa7065a30c66bd64d3"
+                "reference": "2edbc73a4687d9085c8f20f398eebade844e8424"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/filp/whoops/zipball/f6e14679f948d8a5cfb866fa7065a30c66bd64d3",
-                "reference": "f6e14679f948d8a5cfb866fa7065a30c66bd64d3",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/2edbc73a4687d9085c8f20f398eebade844e8424",
+                "reference": "2edbc73a4687d9085c8f20f398eebade844e8424",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/filp/whoops/issues",
-                "source": "https://github.com/filp/whoops/tree/2.11.0"
+                "source": "https://github.com/filp/whoops/tree/2.13.0"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2021-03-19T12:00:00+00:00"
+            "time": "2021-06-04T12:00:00+00:00"
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "7.2.0",
+            "version": "7.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79"
+                "reference": "7008573787b430c1c1f650e3722d9bba59967628"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0aa74dfb41ae110835923ef10a9d803a22d50e79",
-                "reference": "0aa74dfb41ae110835923ef10a9d803a22d50e79",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/7008573787b430c1c1f650e3722d9bba59967628",
+                "reference": "7008573787b430c1c1f650e3722d9bba59967628",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "guzzlehttp/promises": "^1.4",
-                "guzzlehttp/psr7": "^1.7",
+                "guzzlehttp/psr7": "^1.7 || ^2.0",
                 "php": "^7.2.5 || ^8.0",
                 "psr/http-client": "^1.0"
             },
                 "psr/http-client-implementation": "1.0"
             },
             "require-dev": {
+                "bamarni/composer-bin-plugin": "^1.4.1",
                 "ext-curl": "*",
                 "php-http/client-integration-tests": "^3.0",
                 "phpunit/phpunit": "^8.5.5 || ^9.3.5",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "7.1-dev"
+                    "dev-master": "7.3-dev"
                 }
             },
             "autoload": {
             ],
             "support": {
                 "issues": "https://github.com/guzzle/guzzle/issues",
-                "source": "https://github.com/guzzle/guzzle/tree/7.2.0"
+                "source": "https://github.com/guzzle/guzzle/tree/7.3.0"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2020-10-10T11:47:56+00:00"
+            "time": "2021-03-23T11:33:13+00:00"
         },
         {
             "name": "guzzlehttp/promises",
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "1.7.0",
+            "version": "1.8.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3"
+                "reference": "dc960a912984efb74d0a90222870c72c87f10c91"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/53330f47520498c0ae1f61f7e2c90f55690c06a3",
-                "reference": "53330f47520498c0ae1f61f7e2c90f55690c06a3",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/dc960a912984efb74d0a90222870c72c87f10c91",
+                "reference": "dc960a912984efb74d0a90222870c72c87f10c91",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/1.7.0"
+                "source": "https://github.com/guzzle/psr7/tree/1.8.2"
             },
-            "time": "2020-09-30T07:37:11+00:00"
+            "time": "2021-04-26T09:17:50+00:00"
         },
         {
             "name": "intervention/image",
         },
         {
             "name": "laravel/framework",
-            "version": "v6.20.19",
+            "version": "v6.20.29",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "c25308a00c7bbd38401a6254ab403146da9cfd69"
+                "reference": "00fa9c04aed10d68481f5757b89da0e6798f53b3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/c25308a00c7bbd38401a6254ab403146da9cfd69",
-                "reference": "c25308a00c7bbd38401a6254ab403146da9cfd69",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/00fa9c04aed10d68481f5757b89da0e6798f53b3",
+                "reference": "00fa9c04aed10d68481f5757b89da0e6798f53b3",
                 "shasum": ""
             },
             "require": {
                 "issues": "https://github.com/laravel/framework/issues",
                 "source": "https://github.com/laravel/framework"
             },
-            "time": "2021-03-16T14:18:55+00:00"
+            "time": "2021-06-22T13:41:06+00:00"
         },
         {
             "name": "laravel/socialite",
-            "version": "v5.2.2",
+            "version": "v5.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/socialite.git",
-                "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d"
+                "reference": "1960802068f81e44b2ae9793932181cf1cb91b5c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/8d25d574b4f2005411c0b9cb527ef5e745c1b07d",
-                "reference": "8d25d574b4f2005411c0b9cb527ef5e745c1b07d",
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/1960802068f81e44b2ae9793932181cf1cb91b5c",
+                "reference": "1960802068f81e44b2ae9793932181cf1cb91b5c",
                 "shasum": ""
             },
             "require": {
                 "issues": "https://github.com/laravel/socialite/issues",
                 "source": "https://github.com/laravel/socialite"
             },
-            "time": "2021-03-02T16:50:47+00:00"
+            "time": "2021-04-06T14:38:16+00:00"
         },
         {
             "name": "league/commonmark",
-            "version": "1.5.7",
+            "version": "1.6.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/commonmark.git",
-                "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54"
+                "reference": "c3c8b7217c52572fb42aaf84211abccf75a151b2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/11df9b36fd4f1d2b727a73bf14931d81373b9a54",
-                "reference": "11df9b36fd4f1d2b727a73bf14931d81373b9a54",
+                "url": "https://api.github.com/repos/thephpleague/commonmark/zipball/c3c8b7217c52572fb42aaf84211abccf75a151b2",
+                "reference": "c3c8b7217c52572fb42aaf84211abccf75a151b2",
                 "shasum": ""
             },
             "require": {
                 "github/gfm": "0.29.0",
                 "michelf/php-markdown": "~1.4",
                 "mikehaertl/php-shellcommand": "^1.4",
-                "phpstan/phpstan": "^0.12",
+                "phpstan/phpstan": "^0.12.90",
                 "phpunit/phpunit": "^7.5 || ^8.5 || ^9.2",
                 "scrutinizer/ocular": "^1.5",
                 "symfony/finder": "^4.2"
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-10-31T13:49:32+00:00"
+            "time": "2021-06-19T20:08:14+00:00"
         },
         {
             "name": "league/flysystem",
             },
             "time": "2020-10-08T18:58:37+00:00"
         },
+        {
+            "name": "league/html-to-markdown",
+            "version": "5.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/thephpleague/html-to-markdown.git",
+                "reference": "c4dbebbebe0fe454b6b38e6c683a977615bd7dc2"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/thephpleague/html-to-markdown/zipball/c4dbebbebe0fe454b6b38e6c683a977615bd7dc2",
+                "reference": "c4dbebbebe0fe454b6b38e6c683a977615bd7dc2",
+                "shasum": ""
+            },
+            "require": {
+                "ext-dom": "*",
+                "ext-xml": "*",
+                "php": "^7.2.5 || ^8.0"
+            },
+            "require-dev": {
+                "mikehaertl/php-shellcommand": "^1.1.0",
+                "phpstan/phpstan": "^0.12.82",
+                "phpunit/phpunit": "^8.5 || ^9.2",
+                "scrutinizer/ocular": "^1.6",
+                "unleashedtech/php-coding-standard": "^2.7",
+                "vimeo/psalm": "^4.6"
+            },
+            "bin": [
+                "bin/html-to-markdown"
+            ],
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "5.1-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "League\\HTMLToMarkdown\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Colin O'Dell",
+                    "email": "[email protected]",
+                    "homepage": "https://www.colinodell.com",
+                    "role": "Lead Developer"
+                },
+                {
+                    "name": "Nick Cernis",
+                    "email": "[email protected]",
+                    "homepage": "http://modernnerd.net",
+                    "role": "Original Author"
+                }
+            ],
+            "description": "An HTML-to-markdown conversion helper for PHP",
+            "homepage": "https://github.com/thephpleague/html-to-markdown",
+            "keywords": [
+                "html",
+                "markdown"
+            ],
+            "support": {
+                "issues": "https://github.com/thephpleague/html-to-markdown/issues",
+                "source": "https://github.com/thephpleague/html-to-markdown/tree/5.0.0"
+            },
+            "funding": [
+                {
+                    "url": "https://www.colinodell.com/sponsor",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://www.paypal.me/colinpodell/10.00",
+                    "type": "custom"
+                },
+                {
+                    "url": "https://github.com/colinodell",
+                    "type": "github"
+                },
+                {
+                    "url": "https://www.patreon.com/colinodell",
+                    "type": "patreon"
+                }
+            ],
+            "time": "2021-03-29T01:29:08+00:00"
+        },
         {
             "name": "league/mime-type-detection",
             "version": "1.7.0",
         },
         {
             "name": "mtdowling/jmespath.php",
-            "version": "2.6.0",
+            "version": "2.6.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/jmespath/jmespath.php.git",
-                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb"
+                "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/42dae2cbd13154083ca6d70099692fef8ca84bfb",
-                "reference": "42dae2cbd13154083ca6d70099692fef8ca84bfb",
+                "url": "https://api.github.com/repos/jmespath/jmespath.php/zipball/9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
+                "reference": "9b87907a81b87bc76d19a7fb2d61e61486ee9edb",
                 "shasum": ""
             },
             "require": {
                 "symfony/polyfill-mbstring": "^1.17"
             },
             "require-dev": {
-                "composer/xdebug-handler": "^1.4",
+                "composer/xdebug-handler": "^1.4 || ^2.0",
                 "phpunit/phpunit": "^4.8.36 || ^7.5.15"
             },
             "bin": [
             ],
             "support": {
                 "issues": "https://github.com/jmespath/jmespath.php/issues",
-                "source": "https://github.com/jmespath/jmespath.php/tree/2.6.0"
+                "source": "https://github.com/jmespath/jmespath.php/tree/2.6.1"
             },
-            "time": "2020-07-31T21:01:56+00:00"
+            "time": "2021-06-14T00:11:39+00:00"
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.46.0",
+            "version": "2.49.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4"
+                "reference": "93d9db91c0235c486875d22f1e08b50bdf3e6eee"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
-                "reference": "2fd2c4a77d58a4e95234c8a61c5df1f157a91bf4",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/93d9db91c0235c486875d22f1e08b50bdf3e6eee",
+                "reference": "93d9db91c0235c486875d22f1e08b50bdf3e6eee",
                 "shasum": ""
             },
             "require": {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-24T17:30:44+00:00"
+            "time": "2021-06-02T07:31:40+00:00"
         },
         {
             "name": "nunomaduro/collision",
         },
         {
             "name": "opis/closure",
-            "version": "3.6.1",
+            "version": "3.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/opis/closure.git",
-                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5"
+                "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/opis/closure/zipball/943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
-                "reference": "943b5d70cc5ae7483f6aff6ff43d7e34592ca0f5",
+                "url": "https://api.github.com/repos/opis/closure/zipball/06e2ebd25f2869e54a306dda991f7db58066f7f6",
+                "reference": "06e2ebd25f2869e54a306dda991f7db58066f7f6",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/opis/closure/issues",
-                "source": "https://github.com/opis/closure/tree/3.6.1"
+                "source": "https://github.com/opis/closure/tree/3.6.2"
             },
-            "time": "2020-11-07T02:01:34+00:00"
+            "time": "2021-04-09T13:42:10+00:00"
         },
         {
             "name": "paragonie/random_compat",
         },
         {
             "name": "predis/predis",
-            "version": "v1.1.6",
+            "version": "v1.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/predis/predis.git",
-                "reference": "9930e933c67446962997b05201c69c2319bf26de"
+                "reference": "b240daa106d4e02f0c5b7079b41e31ddf66fddf8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/predis/predis/zipball/9930e933c67446962997b05201c69c2319bf26de",
-                "reference": "9930e933c67446962997b05201c69c2319bf26de",
+                "url": "https://api.github.com/repos/predis/predis/zipball/b240daa106d4e02f0c5b7079b41e31ddf66fddf8",
+                "reference": "b240daa106d4e02f0c5b7079b41e31ddf66fddf8",
                 "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/"
             ],
             "support": {
                 "issues": "https://github.com/predis/predis/issues",
-                "source": "https://github.com/predis/predis/tree/v1.1.6"
+                "source": "https://github.com/predis/predis/tree/v1.1.7"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2020-09-11T19:18:05+00:00"
+            "time": "2021-04-04T19:34:46+00:00"
         },
         {
             "name": "psr/container",
         },
         {
             "name": "psr/log",
-            "version": "1.1.3",
+            "version": "1.1.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/log.git",
-                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc"
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/log/zipball/0f73288fd15629204f9d42b7055f72dacbe811fc",
-                "reference": "0f73288fd15629204f9d42b7055f72dacbe811fc",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/d49695b909c3b7628b6289db5479a1c204601f11",
+                "reference": "d49695b909c3b7628b6289db5479a1c204601f11",
                 "shasum": ""
             },
             "require": {
             "authors": [
                 {
                     "name": "PHP-FIG",
-                    "homepage": "http://www.php-fig.org/"
+                    "homepage": "https://www.php-fig.org/"
                 }
             ],
             "description": "Common interface for logging libraries",
                 "psr-3"
             ],
             "support": {
-                "source": "https://github.com/php-fig/log/tree/1.1.3"
+                "source": "https://github.com/php-fig/log/tree/1.1.4"
             },
-            "time": "2020-03-23T09:12:05+00:00"
+            "time": "2021-05-03T11:20:27+00:00"
         },
         {
             "name": "psr/simple-cache",
         },
         {
             "name": "socialiteproviders/microsoft-azure",
-            "version": "4.2.0",
+            "version": "4.2.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
-                "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a"
+                "reference": "64779ec21db0bee3111039a67c0fa0ab550a3462"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/7808764f777a01df88be9ca6b14d683e50aaf88a",
-                "reference": "7808764f777a01df88be9ca6b14d683e50aaf88a",
+                "url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/64779ec21db0bee3111039a67c0fa0ab550a3462",
+                "reference": "64779ec21db0bee3111039a67c0fa0ab550a3462",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
             "support": {
-                "source": "https://github.com/SocialiteProviders/Microsoft-Azure/tree/4.2.0"
+                "source": "https://github.com/SocialiteProviders/Microsoft-Azure/tree/4.2.1"
             },
-            "time": "2020-12-01T23:10:59+00:00"
+            "time": "2021-06-14T22:51:38+00:00"
         },
         {
             "name": "socialiteproviders/okta",
         },
         {
             "name": "socialiteproviders/slack",
-            "version": "4.1.0",
+            "version": "4.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Slack.git",
-                "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc"
+                "reference": "2b781c95daf06ec87a8f3deba2ab613d6bea5e8d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
-                "reference": "8efb25c71d98bedf4010a829d1e41ff9fe449bcc",
+                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/2b781c95daf06ec87a8f3deba2ab613d6bea5e8d",
+                "reference": "2b781c95daf06ec87a8f3deba2ab613d6bea5e8d",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
-                "php": "^7.2|^8.0",
+                "php": "^7.2 || ^8.0",
                 "socialiteproviders/manager": "~4.0"
             },
             "type": "library",
             ],
             "description": "Slack OAuth2 Provider for Laravel Socialite",
             "support": {
-                "source": "https://github.com/SocialiteProviders/Slack/tree/4.1.0"
+                "source": "https://github.com/SocialiteProviders/Slack/tree/4.1.1"
             },
-            "time": "2020-11-26T17:57:15+00:00"
+            "time": "2021-03-26T04:10:10+00:00"
         },
         {
             "name": "socialiteproviders/twitch",
         },
         {
             "name": "symfony/console",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "c98349bda966c70d6c08b4cd8658377c94166492"
+                "reference": "a62acecdf5b50e314a4f305cd01b5282126f3095"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/c98349bda966c70d6c08b4cd8658377c94166492",
-                "reference": "c98349bda966c70d6c08b4cd8658377c94166492",
+                "url": "https://api.github.com/repos/symfony/console/zipball/a62acecdf5b50e314a4f305cd01b5282126f3095",
+                "reference": "a62acecdf5b50e314a4f305cd01b5282126f3095",
                 "shasum": ""
             },
             "require": {
             "description": "Eases the creation of beautiful and testable command line interfaces",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/console/tree/v4.4.20"
+                "source": "https://github.com/symfony/console/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-22T18:44:15+00:00"
+            "time": "2021-05-26T11:20:16+00:00"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8"
+                "reference": "c1e29de6dc893b130b45d20d8051efbb040560a9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f907d3e53ecb2a5fad8609eb2f30525287a734c8",
-                "reference": "f907d3e53ecb2a5fad8609eb2f30525287a734c8",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/c1e29de6dc893b130b45d20d8051efbb040560a9",
+                "reference": "c1e29de6dc893b130b45d20d8051efbb040560a9",
                 "shasum": ""
             },
             "require": {
             "description": "Converts CSS selectors to XPath expressions",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/css-selector/tree/v4.4.20"
+                "source": "https://github.com/symfony/css-selector/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T09:09:26+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "157bbec4fd773bae53c5483c50951a5530a2cc16"
+                "reference": "a8d2d5c94438548bff9f998ca874e202bb29d07f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/157bbec4fd773bae53c5483c50951a5530a2cc16",
-                "reference": "157bbec4fd773bae53c5483c50951a5530a2cc16",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/a8d2d5c94438548bff9f998ca874e202bb29d07f",
+                "reference": "a8d2d5c94438548bff9f998ca874e202bb29d07f",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools to ease debugging PHP code",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/debug/tree/v4.4.20"
+                "source": "https://github.com/symfony/debug/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-28T16:54:48+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
-            "version": "v2.2.0",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/deprecation-contracts.git",
-                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665"
+                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5fa56b4074d1ae755beb55617ddafe6f5d78f665",
-                "reference": "5fa56b4074d1ae755beb55617ddafe6f5d78f665",
+                "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
+                "reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
             "description": "A generic function and convention to trigger deprecation notices",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/deprecation-contracts/tree/master"
+                "source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-07T11:33:47+00:00"
+            "time": "2021-03-23T23:28:01+00:00"
         },
         {
             "name": "symfony/error-handler",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/error-handler.git",
-                "reference": "a191550d46b73a527b9d244f185fef439d41cf15"
+                "reference": "310a756cec00d29d89a08518405aded046a54a8b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/error-handler/zipball/a191550d46b73a527b9d244f185fef439d41cf15",
-                "reference": "a191550d46b73a527b9d244f185fef439d41cf15",
+                "url": "https://api.github.com/repos/symfony/error-handler/zipball/310a756cec00d29d89a08518405aded046a54a8b",
+                "reference": "310a756cec00d29d89a08518405aded046a54a8b",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools to manage errors and ease debugging PHP code",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/error-handler/tree/v4.4.20"
+                "source": "https://github.com/symfony/error-handler/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-11T08:19:35+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c"
+                "reference": "047773e7016e4fd45102cedf4bd2558ae0d0c32f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/c352647244bd376bf7d31efbd5401f13f50dad0c",
-                "reference": "c352647244bd376bf7d31efbd5401f13f50dad0c",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/047773e7016e4fd45102cedf4bd2558ae0d0c32f",
+                "reference": "047773e7016e4fd45102cedf4bd2558ae0d0c32f",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.20"
+                "source": "https://github.com/symfony/event-dispatcher/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T09:09:26+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
         },
         {
             "name": "symfony/finder",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6"
+                "reference": "ed33314396d968a8936c95f5bd1b88bd3b3e94a3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/2543795ab1570df588b9bbd31e1a2bd7037b94f6",
-                "reference": "2543795ab1570df588b9bbd31e1a2bd7037b94f6",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/ed33314396d968a8936c95f5bd1b88bd3b3e94a3",
+                "reference": "ed33314396d968a8936c95f5bd1b88bd3b3e94a3",
                 "shasum": ""
             },
             "require": {
             "description": "Finds files and directories via an intuitive fluent interface",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/finder/tree/v4.4.20"
+                "source": "https://github.com/symfony/finder/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-12T10:48:09+00:00"
+            "time": "2021-05-26T11:20:16+00:00"
         },
         {
             "name": "symfony/http-client-contracts",
-            "version": "v2.3.1",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-client-contracts.git",
-                "reference": "41db680a15018f9c1d4b23516059633ce280ca33"
+                "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/41db680a15018f9c1d4b23516059633ce280ca33",
-                "reference": "41db680a15018f9c1d4b23516059633ce280ca33",
+                "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/7e82f6084d7cae521a75ef2cb5c9457bbda785f4",
+                "reference": "7e82f6084d7cae521a75ef2cb5c9457bbda785f4",
                 "shasum": ""
             },
             "require": {
             },
             "type": "library",
             "extra": {
-                "branch-version": "2.3",
                 "branch-alias": {
-                    "dev-main": "2.3-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/http-client-contracts/tree/v2.3.1"
+                "source": "https://github.com/symfony/http-client-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-10-14T17:08:19+00:00"
+            "time": "2021-04-11T23:07:08+00:00"
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a"
+                "reference": "0c79d5a65ace4fe66e49702658c024a419d2438b"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/02d968647fe61b2f419a8dc70c468a9d30a48d3a",
-                "reference": "02d968647fe61b2f419a8dc70c468a9d30a48d3a",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/0c79d5a65ace4fe66e49702658c024a419d2438b",
+                "reference": "0c79d5a65ace4fe66e49702658c024a419d2438b",
                 "shasum": ""
             },
             "require": {
             "description": "Defines an object-oriented layer for the HTTP specification",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-foundation/tree/v4.4.20"
+                "source": "https://github.com/symfony/http-foundation/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-25T17:11:33+00:00"
+            "time": "2021-05-26T11:20:16+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "4f36548465489f293b05406f1770492f6efb8adb"
+                "reference": "3795165596fe81a52296b78c9aae938d434069cc"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/4f36548465489f293b05406f1770492f6efb8adb",
-                "reference": "4f36548465489f293b05406f1770492f6efb8adb",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/3795165596fe81a52296b78c9aae938d434069cc",
+                "reference": "3795165596fe81a52296b78c9aae938d434069cc",
                 "shasum": ""
             },
             "require": {
             "description": "Provides a structured process for converting a Request into a Response",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/http-kernel/tree/v4.4.20"
+                "source": "https://github.com/symfony/http-kernel/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-03-04T18:00:27+00:00"
+            "time": "2021-06-01T07:12:08+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v5.2.5",
+            "version": "v5.3.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139"
+                "reference": "47dd7912152b82d0d4c8d9040dbc93d6232d472a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/554ba128f1955038b45db5e1fa7e93bfc683b139",
-                "reference": "554ba128f1955038b45db5e1fa7e93bfc683b139",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/47dd7912152b82d0d4c8d9040dbc93d6232d472a",
+                "reference": "47dd7912152b82d0d4c8d9040dbc93d6232d472a",
                 "shasum": ""
             },
             "require": {
                 "mime-type"
             ],
             "support": {
-                "source": "https://github.com/symfony/mime/tree/v5.2.5"
+                "source": "https://github.com/symfony/mime/tree/v5.3.2"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-03-07T16:08:20+00:00"
+            "time": "2021-06-09T10:58:01+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-ctype.git",
-                "reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
+                "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
-                "reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
+                "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
+                "reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "portable"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-ctype/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-07T16:49:33+00:00"
+            "time": "2021-02-19T12:13:01+00:00"
         },
         {
             "name": "symfony/polyfill-iconv",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-iconv.git",
-                "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342"
+                "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/06fb361659649bcfd6a208a0f1fcaf4e827ad342",
-                "reference": "06fb361659649bcfd6a208a0f1fcaf4e827ad342",
+                "url": "https://api.github.com/repos/symfony/polyfill-iconv/zipball/63b5bb7db83e5673936d6e3b8b3e022ff6474933",
+                "reference": "63b5bb7db83e5673936d6e3b8b3e022ff6474933",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-iconv/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-iconv/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-22T09:19:47+00:00"
+            "time": "2021-05-27T09:27:20+00:00"
         },
         {
             "name": "symfony/polyfill-intl-idn",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-idn.git",
-                "reference": "2d63434d922daf7da8dd863e7907e67ee3031483"
+                "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/2d63434d922daf7da8dd863e7907e67ee3031483",
-                "reference": "2d63434d922daf7da8dd863e7907e67ee3031483",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/65bd267525e82759e7d8c4e8ceea44f398838e65",
+                "reference": "65bd267525e82759e7d8c4e8ceea44f398838e65",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-22T09:19:47+00:00"
+            "time": "2021-05-27T09:27:20+00:00"
         },
         {
             "name": "symfony/polyfill-intl-normalizer",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-intl-normalizer.git",
-                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248"
+                "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/43a0283138253ed1d48d352ab6d0bdb3f809f248",
-                "reference": "43a0283138253ed1d48d352ab6d0bdb3f809f248",
+                "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
+                "reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-22T09:19:47+00:00"
+            "time": "2021-02-19T12:13:01+00:00"
         },
         {
             "name": "symfony/polyfill-mbstring",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-mbstring.git",
-                "reference": "5232de97ee3b75b0360528dae24e73db49566ab1"
+                "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/5232de97ee3b75b0360528dae24e73db49566ab1",
-                "reference": "5232de97ee3b75b0360528dae24e73db49566ab1",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
+                "reference": "2df51500adbaebdc4c38dea4c89a2e131c45c8a1",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-22T09:19:47+00:00"
+            "time": "2021-05-27T09:27:20+00:00"
         },
         {
             "name": "symfony/polyfill-php72",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php72.git",
-                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9"
+                "reference": "9a142215a36a3888e30d0a9eeea9766764e96976"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
-                "reference": "cc6e6f9b39fe8075b3dabfbaf5b5f645ae1340c9",
+                "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/9a142215a36a3888e30d0a9eeea9766764e96976",
+                "reference": "9a142215a36a3888e30d0a9eeea9766764e96976",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php72/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-php72/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-07T16:49:33+00:00"
+            "time": "2021-05-27T09:17:38+00:00"
         },
         {
             "name": "symfony/polyfill-php73",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php73.git",
-                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2"
+                "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
-                "reference": "a678b42e92f86eca04b7fa4c0f6f19d097fb69e2",
+                "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fba8933c384d6476ab14fb7b8526e5287ca7e010",
+                "reference": "fba8933c384d6476ab14fb7b8526e5287ca7e010",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php73/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-php73/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-07T16:49:33+00:00"
+            "time": "2021-02-19T12:13:01+00:00"
         },
         {
             "name": "symfony/polyfill-php80",
-            "version": "v1.22.1",
+            "version": "v1.23.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/polyfill-php80.git",
-                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91"
+                "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/dc3063ba22c2a1fd2f45ed856374d79114998f91",
-                "reference": "dc3063ba22c2a1fd2f45ed856374d79114998f91",
+                "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/eca0bf41ed421bed1b57c4958bab16aa86b757d0",
+                "reference": "eca0bf41ed421bed1b57c4958bab16aa86b757d0",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-main": "1.22-dev"
+                    "dev-main": "1.23-dev"
                 },
                 "thanks": {
                     "name": "symfony/polyfill",
                 "shim"
             ],
             "support": {
-                "source": "https://github.com/symfony/polyfill-php80/tree/v1.22.1"
+                "source": "https://github.com/symfony/polyfill-php80/tree/v1.23.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-07T16:49:33+00:00"
+            "time": "2021-02-19T12:13:01+00:00"
         },
         {
             "name": "symfony/process",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a"
+                "reference": "cd61e6dd273975c6625316de9d141ebd197f93c9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/7e950b6366d4da90292c2e7fa820b3c1842b965a",
-                "reference": "7e950b6366d4da90292c2e7fa820b3c1842b965a",
+                "url": "https://api.github.com/repos/symfony/process/zipball/cd61e6dd273975c6625316de9d141ebd197f93c9",
+                "reference": "cd61e6dd273975c6625316de9d141ebd197f93c9",
                 "shasum": ""
             },
             "require": {
             "description": "Executes commands in sub-processes",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/process/tree/v4.4.20"
+                "source": "https://github.com/symfony/process/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T09:09:26+00:00"
+            "time": "2021-05-26T11:20:16+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "69919991c845b34626664ddc9b3aef9d09d2a5df"
+                "reference": "3a3c2f197ad0846ac6413225fc78868ba1c61434"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/69919991c845b34626664ddc9b3aef9d09d2a5df",
-                "reference": "69919991c845b34626664ddc9b3aef9d09d2a5df",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/3a3c2f197ad0846ac6413225fc78868ba1c61434",
+                "reference": "3a3c2f197ad0846ac6413225fc78868ba1c61434",
                 "shasum": ""
             },
             "require": {
                 "url"
             ],
             "support": {
-                "source": "https://github.com/symfony/routing/tree/v4.4.20"
+                "source": "https://github.com/symfony/routing/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-22T15:37:04+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v2.2.0",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1"
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/d15da7ba4957ffb8f1747218be9e1a121fd298a1",
-                "reference": "d15da7ba4957ffb8f1747218be9e1a121fd298a1",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
+                "reference": "f040a30e04b57fbcc9c6cbcf4dbaa96bd318b9bb",
                 "shasum": ""
             },
             "require": {
                 "php": ">=7.2.5",
-                "psr/container": "^1.0"
+                "psr/container": "^1.1"
             },
             "suggest": {
                 "symfony/service-implementation": ""
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.2-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/service-contracts/tree/master"
+                "source": "https://github.com/symfony/service-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-07T11:33:47+00:00"
+            "time": "2021-04-01T10:43:52+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "2271b6d577018a7dea75a9162a08ac84f8234deb"
+                "reference": "dfe132c5c6d89f90ce7f961742cc532e9ca16dd4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/2271b6d577018a7dea75a9162a08ac84f8234deb",
-                "reference": "2271b6d577018a7dea75a9162a08ac84f8234deb",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/dfe132c5c6d89f90ce7f961742cc532e9ca16dd4",
+                "reference": "dfe132c5c6d89f90ce7f961742cc532e9ca16dd4",
                 "shasum": ""
             },
             "require": {
             "description": "Provides tools to internationalize your application",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/translation/tree/v4.4.20"
+                "source": "https://github.com/symfony/translation/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-26T13:53:48+00:00"
+            "time": "2021-05-26T17:39:37+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v2.3.0",
+            "version": "v2.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105"
+                "reference": "95c812666f3e91db75385749fe219c5e494c7f95"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/e2eaa60b558f26a4b0354e1bbb25636efaaad105",
-                "reference": "e2eaa60b558f26a4b0354e1bbb25636efaaad105",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/95c812666f3e91db75385749fe219c5e494c7f95",
+                "reference": "95c812666f3e91db75385749fe219c5e494c7f95",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.3-dev"
+                    "dev-main": "2.4-dev"
                 },
                 "thanks": {
                     "name": "symfony/contracts",
                 "standards"
             ],
             "support": {
-                "source": "https://github.com/symfony/translation-contracts/tree/v2.3.0"
+                "source": "https://github.com/symfony/translation-contracts/tree/v2.4.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-09-28T13:05:58+00:00"
+            "time": "2021-03-23T23:28:01+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f"
+                "reference": "31ea689a8e7d2410016b0d25fc15a1ba05a6e2e0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
-                "reference": "a1eab2f69906dc83c5ddba4632180260d0ab4f7f",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/31ea689a8e7d2410016b0d25fc15a1ba05a6e2e0",
+                "reference": "31ea689a8e7d2410016b0d25fc15a1ba05a6e2e0",
                 "shasum": ""
             },
             "require": {
                 "dump"
             ],
             "support": {
-                "source": "https://github.com/symfony/var-dumper/tree/v4.4.20"
+                "source": "https://github.com/symfony/var-dumper/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-27T09:09:26+00:00"
+            "time": "2021-05-27T09:48:32+00:00"
         },
         {
             "name": "tijsverkoyen/css-to-inline-styles",
     "packages-dev": [
         {
             "name": "barryvdh/laravel-debugbar",
-            "version": "v3.5.2",
+            "version": "v3.6.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-debugbar.git",
-                "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b"
+                "reference": "70b89754913fd89fef16d0170a91dbc2a5cd633a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/cae0a8d1cb89b0f0522f65e60465e16d738e069b",
-                "reference": "cae0a8d1cb89b0f0522f65e60465e16d738e069b",
+                "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/70b89754913fd89fef16d0170a91dbc2a5cd633a",
+                "reference": "70b89754913fd89fef16d0170a91dbc2a5cd633a",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/barryvdh/laravel-debugbar/issues",
-                "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.5.2"
+                "source": "https://github.com/barryvdh/laravel-debugbar/tree/v3.6.2"
             },
             "funding": [
+                {
+                    "url": "https://fruitcake.nl",
+                    "type": "custom"
+                },
                 {
                     "url": "https://github.com/barryvdh",
                     "type": "github"
                 }
             ],
-            "time": "2021-01-06T14:21:44+00:00"
+            "time": "2021-06-14T14:29:26+00:00"
         },
         {
             "name": "barryvdh/laravel-ide-helper",
         },
         {
             "name": "composer/ca-bundle",
-            "version": "1.2.9",
+            "version": "1.2.10",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/ca-bundle.git",
-                "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5"
+                "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
-                "reference": "78a0e288fdcebf92aa2318a8d3656168da6ac1a5",
+                "url": "https://api.github.com/repos/composer/ca-bundle/zipball/9fdb22c2e97a614657716178093cd1da90a64aa8",
+                "reference": "9fdb22c2e97a614657716178093cd1da90a64aa8",
                 "shasum": ""
             },
             "require": {
             "support": {
                 "irc": "irc://irc.freenode.org/composer",
                 "issues": "https://github.com/composer/ca-bundle/issues",
-                "source": "https://github.com/composer/ca-bundle/tree/1.2.9"
+                "source": "https://github.com/composer/ca-bundle/tree/1.2.10"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-01-12T12:10:35+00:00"
+            "time": "2021-06-07T13:58:28+00:00"
         },
         {
             "name": "composer/composer",
-            "version": "2.0.11",
+            "version": "2.1.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/composer.git",
-                "reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9"
+                "reference": "fc5c4573aafce3a018eb7f1f8f91cea423970f2e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/composer/zipball/a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9",
-                "reference": "a5a5632da0b1c2d6fa9a3b65f1f4e90d1f04abb9",
+                "url": "https://api.github.com/repos/composer/composer/zipball/fc5c4573aafce3a018eb7f1f8f91cea423970f2e",
+                "reference": "fc5c4573aafce3a018eb7f1f8f91cea423970f2e",
                 "shasum": ""
             },
             "require": {
                 "composer/ca-bundle": "^1.0",
+                "composer/metadata-minifier": "^1.0",
                 "composer/semver": "^3.0",
                 "composer/spdx-licenses": "^1.2",
-                "composer/xdebug-handler": "^1.1",
+                "composer/xdebug-handler": "^2.0",
                 "justinrainbow/json-schema": "^5.2.10",
                 "php": "^5.3.2 || ^7.0 || ^8.0",
                 "psr/log": "^1.0",
                 "react/promise": "^1.2 || ^2.7",
                 "seld/jsonlint": "^1.4",
                 "seld/phar-utils": "^1.0",
-                "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
-                "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
-                "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0",
-                "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0"
+                "symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
+                "symfony/filesystem": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
+                "symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
+                "symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
             },
             "require-dev": {
                 "phpspec/prophecy": "^1.10",
-                "symfony/phpunit-bridge": "^4.2 || ^5.0"
+                "symfony/phpunit-bridge": "^4.2 || ^5.0 || ^6.0"
             },
             "suggest": {
                 "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.0-dev"
+                    "dev-master": "2.1-dev"
                 }
             },
             "autoload": {
             "support": {
                 "irc": "irc://irc.freenode.org/composer",
                 "issues": "https://github.com/composer/composer/issues",
-                "source": "https://github.com/composer/composer/tree/2.0.11"
+                "source": "https://github.com/composer/composer/tree/2.1.3"
+            },
+            "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": "2021-06-09T14:31:20+00:00"
+        },
+        {
+            "name": "composer/metadata-minifier",
+            "version": "1.0.0",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/composer/metadata-minifier.git",
+                "reference": "c549d23829536f0d0e984aaabbf02af91f443207"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207",
+                "reference": "c549d23829536f0d0e984aaabbf02af91f443207",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.3.2 || ^7.0 || ^8.0"
+            },
+            "require-dev": {
+                "composer/composer": "^2",
+                "phpstan/phpstan": "^0.12.55",
+                "symfony/phpunit-bridge": "^4.2 || ^5"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "1.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Composer\\MetadataMinifier\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Jordi Boggiano",
+                    "email": "[email protected]",
+                    "homepage": "http://seld.be"
+                }
+            ],
+            "description": "Small utility library that handles metadata minification and expansion.",
+            "keywords": [
+                "composer",
+                "compression"
+            ],
+            "support": {
+                "issues": "https://github.com/composer/metadata-minifier/issues",
+                "source": "https://github.com/composer/metadata-minifier/tree/1.0.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-24T13:57:23+00:00"
+            "time": "2021-04-07T13:37:33+00:00"
         },
         {
             "name": "composer/semver",
-            "version": "3.2.4",
+            "version": "3.2.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/semver.git",
-                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464"
+                "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/semver/zipball/a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
-                "reference": "a02fdf930a3c1c3ed3a49b5f63859c0c20e10464",
+                "url": "https://api.github.com/repos/composer/semver/zipball/31f3ea725711245195f62e54ffa402d8ef2fdba9",
+                "reference": "31f3ea725711245195f62e54ffa402d8ef2fdba9",
                 "shasum": ""
             },
             "require": {
             "support": {
                 "irc": "irc://irc.freenode.org/composer",
                 "issues": "https://github.com/composer/semver/issues",
-                "source": "https://github.com/composer/semver/tree/3.2.4"
+                "source": "https://github.com/composer/semver/tree/3.2.5"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-11-13T08:59:24+00:00"
+            "time": "2021-05-24T12:41:47+00:00"
         },
         {
             "name": "composer/spdx-licenses",
         },
         {
             "name": "composer/xdebug-handler",
-            "version": "1.4.5",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/xdebug-handler.git",
-                "reference": "f28d44c286812c714741478d968104c5e604a1d4"
+                "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/f28d44c286812c714741478d968104c5e604a1d4",
-                "reference": "f28d44c286812c714741478d968104c5e604a1d4",
+                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
+                "reference": "964adcdd3a28bf9ed5d9ac6450064e0d71ed7496",
                 "shasum": ""
             },
             "require": {
                 "psr/log": "^1.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
+                "phpstan/phpstan": "^0.12.55",
+                "symfony/phpunit-bridge": "^4.2 || ^5"
             },
             "type": "library",
             "autoload": {
             "support": {
                 "irc": "irc://irc.freenode.org/composer",
                 "issues": "https://github.com/composer/xdebug-handler/issues",
-                "source": "https://github.com/composer/xdebug-handler/tree/1.4.5"
+                "source": "https://github.com/composer/xdebug-handler/tree/2.0.1"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2020-11-13T08:04:11+00:00"
+            "time": "2021-05-05T19:37:51+00:00"
         },
         {
             "name": "doctrine/instantiator",
         },
         {
             "name": "fakerphp/faker",
-            "version": "v1.13.0",
+            "version": "v1.14.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/FakerPHP/Faker.git",
-                "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913"
+                "reference": "ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ab3f5364d01f2c2c16113442fb987d26e4004913",
-                "reference": "ab3f5364d01f2c2c16113442fb987d26e4004913",
+                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1",
+                "reference": "ed22aee8d17c7b396f74a58b1e7fefa4f90d5ef1",
                 "shasum": ""
             },
             "require": {
-                "php": "^7.1 || ^8.0"
+                "php": "^7.1 || ^8.0",
+                "psr/container": "^1.0",
+                "symfony/deprecation-contracts": "^2.2"
             },
             "conflict": {
                 "fzaninotto/faker": "*"
             "require-dev": {
                 "bamarni/composer-bin-plugin": "^1.4.1",
                 "ext-intl": "*",
-                "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.4.2"
+                "symfony/phpunit-bridge": "^4.4 || ^5.2"
+            },
+            "suggest": {
+                "ext-curl": "Required by Faker\\Provider\\Image to download images.",
+                "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.",
+                "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.",
+                "ext-mbstring": "Required for multibyte Unicode string functionality."
             },
             "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-main": "v1.15-dev"
+                }
+            },
             "autoload": {
                 "psr-4": {
                     "Faker\\": "src/Faker/"
             ],
             "support": {
                 "issues": "https://github.com/FakerPHP/Faker/issues",
-                "source": "https://github.com/FakerPHP/Faker/tree/v1.13.0"
+                "source": "https://github.com/FakerPHP/Faker/tree/v.1.14.1"
             },
-            "time": "2020-12-18T16:50:48+00:00"
+            "time": "2021-03-30T06:27:33+00:00"
         },
         {
             "name": "hamcrest/hamcrest-php",
         },
         {
             "name": "nikic/php-parser",
-            "version": "v4.10.4",
+            "version": "v4.10.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/nikic/PHP-Parser.git",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e"
+                "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/c6d052fc58cb876152f89f532b95a8d7907e7f0e",
-                "reference": "c6d052fc58cb876152f89f532b95a8d7907e7f0e",
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/4432ba399e47c66624bc73c8c0f811e5c109576f",
+                "reference": "4432ba399e47c66624bc73c8c0f811e5c109576f",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.4"
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.10.5"
             },
-            "time": "2020-12-20T10:01:03+00:00"
+            "time": "2021-05-03T19:11:20+00:00"
         },
         {
             "name": "phar-io/manifest",
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "9.2.5",
+            "version": "9.2.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1"
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f3e026641cc91909d421802dd3ac7827ebfd97e1",
-                "reference": "f3e026641cc91909d421802dd3ac7827ebfd97e1",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
+                "reference": "f6293e1b30a2354e8428e004689671b83871edde",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.5"
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2020-11-28T06:44:49+00:00"
+            "time": "2021-03-28T07:26:59+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.5.3",
+            "version": "9.5.5",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41"
+                "reference": "89ff45ea9d70e35522fb6654a2ebc221158de276"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/27241ac75fc37ecf862b6e002bf713b6566cbe41",
-                "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/89ff45ea9d70e35522fb6654a2ebc221158de276",
+                "reference": "89ff45ea9d70e35522fb6654a2ebc221158de276",
                 "shasum": ""
             },
             "require": {
                 "sebastian/global-state": "^5.0.1",
                 "sebastian/object-enumerator": "^4.0.3",
                 "sebastian/resource-operations": "^3.0.3",
-                "sebastian/type": "^2.3",
+                "sebastian/type": "^2.3.2",
                 "sebastian/version": "^3.0.2"
             },
             "require-dev": {
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.3"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.5"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2021-03-17T07:30:34+00:00"
+            "time": "2021-06-05T04:49:07+00:00"
         },
         {
             "name": "react/promise",
         },
         {
             "name": "sebastian/global-state",
-            "version": "5.0.2",
+            "version": "5.0.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/global-state.git",
-                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455"
+                "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/a90ccbddffa067b51f574dea6eb25d5680839455",
-                "reference": "a90ccbddffa067b51f574dea6eb25d5680839455",
+                "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/23bd5951f7ff26f12d4e3242864df3e08dec4e49",
+                "reference": "23bd5951f7ff26f12d4e3242864df3e08dec4e49",
                 "shasum": ""
             },
             "require": {
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/global-state/issues",
-                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.2"
+                "source": "https://github.com/sebastianbergmann/global-state/tree/5.0.3"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T15:55:19+00:00"
+            "time": "2021-06-11T13:31:12+00:00"
         },
         {
             "name": "sebastian/lines-of-code",
         },
         {
             "name": "sebastian/type",
-            "version": "2.3.1",
+            "version": "2.3.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/type.git",
-                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2"
+                "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
-                "reference": "81cd61ab7bbf2de744aba0ea61fae32f721df3d2",
+                "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b8cd8a1c753c90bc1a0f5372170e3e489136f914",
+                "reference": "b8cd8a1c753c90bc1a0f5372170e3e489136f914",
                 "shasum": ""
             },
             "require": {
             "homepage": "https://github.com/sebastianbergmann/type",
             "support": {
                 "issues": "https://github.com/sebastianbergmann/type/issues",
-                "source": "https://github.com/sebastianbergmann/type/tree/2.3.1"
+                "source": "https://github.com/sebastianbergmann/type/tree/2.3.4"
             },
             "funding": [
                 {
                     "type": "github"
                 }
             ],
-            "time": "2020-10-26T13:18:59+00:00"
+            "time": "2021-06-15T12:49:02+00:00"
         },
         {
             "name": "sebastian/version",
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.5.8",
+            "version": "3.6.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4"
+                "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/9d583721a7157ee997f235f327de038e7ea6dac4",
-                "reference": "9d583721a7157ee997f235f327de038e7ea6dac4",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ffced0d2c8fa8e6cdc4d695a743271fab6c38625",
+                "reference": "ffced0d2c8fa8e6cdc4d695a743271fab6c38625",
                 "shasum": ""
             },
             "require": {
                 "source": "https://github.com/squizlabs/PHP_CodeSniffer",
                 "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki"
             },
-            "time": "2020-10-23T02:01:07+00:00"
+            "time": "2021-04-09T00:54:41+00:00"
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.4.20",
+            "version": "v4.4.25",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "be133557f1b0e6672367325b508e65da5513a311"
+                "reference": "41d15bb6d6b95d2be763c514bb2494215d9c5eef"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/be133557f1b0e6672367325b508e65da5513a311",
-                "reference": "be133557f1b0e6672367325b508e65da5513a311",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/41d15bb6d6b95d2be763c514bb2494215d9c5eef",
+                "reference": "41d15bb6d6b95d2be763c514bb2494215d9c5eef",
                 "shasum": ""
             },
             "require": {
             "description": "Eases DOM navigation for HTML and XML documents",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/dom-crawler/tree/v4.4.20"
+                "source": "https://github.com/symfony/dom-crawler/tree/v4.4.25"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-14T12:29:41+00:00"
+            "time": "2021-05-26T11:20:16+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v5.2.4",
+            "version": "v5.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
-                "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108"
+                "reference": "348116319d7fb7d1faa781d26a48922428013eb2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/filesystem/zipball/710d364200997a5afde34d9fe57bd52f3cc1e108",
-                "reference": "710d364200997a5afde34d9fe57bd52f3cc1e108",
+                "url": "https://api.github.com/repos/symfony/filesystem/zipball/348116319d7fb7d1faa781d26a48922428013eb2",
+                "reference": "348116319d7fb7d1faa781d26a48922428013eb2",
                 "shasum": ""
             },
             "require": {
             "description": "Provides basic utilities for the filesystem",
             "homepage": "https://symfony.com",
             "support": {
-                "source": "https://github.com/symfony/filesystem/tree/v5.2.4"
+                "source": "https://github.com/symfony/filesystem/tree/v5.3.0"
             },
             "funding": [
                 {
                     "type": "tidelift"
                 }
             ],
-            "time": "2021-02-12T10:38:38+00:00"
+            "time": "2021-05-26T17:43:10+00:00"
         },
         {
             "name": "theseer/tokenizer",
         "php": "^7.3|^8.0",
         "ext-curl": "*",
         "ext-dom": "*",
+        "ext-fileinfo": "*",
         "ext-gd": "*",
         "ext-json": "*",
         "ext-mbstring": "*",
index 7398ed39885cedde9655f9fb06783016e6c6b75e..a066fb1309445e378f7880457bfd57fceac28498 100644 (file)
@@ -15,7 +15,7 @@ class CreateSearchIndexTable extends Migration
     {
         Schema::create('search_terms', function (Blueprint $table) {
             $table->increments('id');
-            $table->string('term', 200);
+            $table->string('term', 180);
             $table->string('entity_type', 100);
             $table->integer('entity_id');
             $table->integer('score');
index 706a883a387d996055eebc7742ebb3e56f003fe3..0778e8762dd245e1855c675bf48e4de74bad1621 100644 (file)
@@ -14,7 +14,7 @@ class AddRoleExternalAuthId extends Migration
     public function up()
     {
         Schema::table('roles', function (Blueprint $table) {
-            $table->string('external_auth_id', 200)->default('');
+            $table->string('external_auth_id', 180)->default('');
             $table->index('external_auth_id');
         });
     }
index 9efba0071c3689c6fe5ce13cdc8faa59ae0f7142..488c6196830c4549b43b8a3d9fb4517eef7704fc 100644 (file)
@@ -37,8 +37,8 @@ class CreateBookshelvesTable extends Migration
 
         Schema::create('bookshelves', function (Blueprint $table) {
             $table->increments('id');
-            $table->string('name', 200);
-            $table->string('slug', 200);
+            $table->string('name', 180);
+            $table->string('slug', 180);
             $table->text('description');
             $table->integer('created_by')->nullable()->default(null);
             $table->integer('updated_by')->nullable()->default(null);
index 906e06b952297e2e5a3557f9ee8d3273c20811f6..dad1e42273c29d551b6be14bf14db25a1b3132eb 100644 (file)
@@ -15,7 +15,7 @@ class AddUserSlug extends Migration
     public function up()
     {
         Schema::table('users', function (Blueprint $table) {
-            $table->string('slug', 250);
+            $table->string('slug', 180);
         });
 
         $slugMap = [];
diff --git a/database/migrations/2021_05_15_173110_create_favourites_table.php b/database/migrations/2021_05_15_173110_create_favourites_table.php
new file mode 100644 (file)
index 0000000..783bf58
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+class CreateFavouritesTable extends Migration
+{
+    /**
+     * Run the migrations.
+     *
+     * @return void
+     */
+    public function up()
+    {
+        Schema::create('favourites', function (Blueprint $table) {
+            $table->increments('id');
+            $table->integer('user_id')->index();
+            $table->integer('favouritable_id');
+            $table->string('favouritable_type', 100);
+            $table->timestamps();
+
+            $table->index(['favouritable_id', 'favouritable_type'], 'favouritable_index');
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     *
+     * @return void
+     */
+    public function down()
+    {
+        Schema::dropIfExists('favourites');
+    }
+}
index 895ad595ae7b73cf4077a76bf717092c5146a2ff..178ea9a6c865481f40d07529de0915d86553152e 100644 (file)
@@ -5,9 +5,9 @@ WORKDIR /app
 
 # Install additional dependacnies and configure apache
 RUN apt-get update -y \
-    && apt-get install -y git zip unzip libpng-dev libldap2-dev wait-for-it \
+    && apt-get install -y git zip unzip libpng-dev libldap2-dev libzip-dev wait-for-it \
     && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \
-    && docker-php-ext-install pdo_mysql gd ldap \
+    && docker-php-ext-install pdo_mysql gd ldap zip \
     && a2enmod rewrite \
     && sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
     && sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
@@ -20,4 +20,4 @@ RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
 
 # Use the default production configuration and update it as required
 RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \
-    && sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini"
\ No newline at end of file
+    && sed -i 's/memory_limit = 128M/memory_limit = 512M/g' "$PHP_INI_DIR/php.ini"
index fc8e6646fc688deef58a76f60d20e22d5659958f..b950d7df95cb656cc51506f4d2bdd64fab2e75e3 100644 (file)
@@ -95,4 +95,18 @@ Theme::listen(ThemeEvents::APP_BOOT, function($app) {
         'name' => 'Reddit',
     ], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle');
 });
+```
+
+In some cases you may need to customize the driver before it performs a redirect. 
+This can be done by providing a callback as a fourth parameter like so:
+
+```php
+Theme::addSocialDriver('reddit', [
+    'client_id' => 'abc123',
+    'client_secret' => 'def456789',
+    'name' => 'Reddit',
+], '\SocialiteProviders\Reddit\RedditExtendSocialite@handle', function($driver) {
+    $driver->with(['prompt' => 'select_account']);
+    $driver->scopes(['open_id']);
+});
 ```
\ No newline at end of file
index 7f83f7160878ace0c3e8ac1e5f6ea6e681eec9fa..7d9318363a51f5e8da1d8d811859efa24473416a 100644 (file)
 {
+  "name": "bookstack",
+  "lockfileVersion": 2,
   "requires": true,
-  "lockfileVersion": 1,
+  "packages": {
+    "": {
+      "dependencies": {
+        "clipboard": "^2.0.8",
+        "codemirror": "^5.61.1",
+        "dropzone": "^5.9.2",
+        "markdown-it": "^12.0.6",
+        "markdown-it-task-lists": "^2.1.1",
+        "sortablejs": "^1.13.0"
+      },
+      "devDependencies": {
+        "chokidar-cli": "^2.1.0",
+        "esbuild": "0.12.8",
+        "livereload": "^0.9.3",
+        "npm-run-all": "^4.1.5",
+        "punycode": "^2.1.1",
+        "sass": "^1.34.1"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ansi-styles": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+      "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+      "dev": true,
+      "dependencies": {
+        "color-convert": "^1.9.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/anymatch": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz",
+      "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==",
+      "dev": true,
+      "dependencies": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      },
+      "engines": {
+        "node": ">= 8"
+      }
+    },
+    "node_modules/argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+    },
+    "node_modules/balanced-match": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
+      "dev": true
+    },
+    "node_modules/binary-extensions": {
+      "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,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/brace-expansion": {
+      "version": "1.1.11",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+      "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+      "dev": true,
+      "dependencies": {
+        "balanced-match": "^1.0.0",
+        "concat-map": "0.0.1"
+      }
+    },
+    "node_modules/braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "dependencies": {
+        "fill-range": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/camelcase": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
+      "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "ansi-styles": "^3.2.1",
+        "escape-string-regexp": "^1.0.5",
+        "supports-color": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/chokidar": {
+      "version": "3.4.2",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.2.tgz",
+      "integrity": "sha512-IZHaDeBeI+sZJRX7lGcXsdzgvZqKv6sECqsbErJA4mHWfpRrD8B97kSFN4cQz6nGBGiuFia1MKR4d6c1o8Cv7A==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.4.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.1.2"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "chokidar": "^3.2.3",
+        "lodash.debounce": "^4.0.8",
+        "lodash.throttle": "^4.1.1",
+        "yargs": "^13.3.0"
+      },
+      "bin": {
+        "chokidar": "index.js"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      }
+    },
+    "node_modules/clipboard": {
+      "version": "2.0.8",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.8.tgz",
+      "integrity": "sha512-Y6WO0unAIQp5bLmk1zdThRhgJt/x3ks6f30s3oE3H1mgIEU33XyQjEf8gsf6DxC7NPX8Y1SsNWjUjL/ywLnnbQ==",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "node_modules/cliui": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+      "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^3.1.0",
+        "strip-ansi": "^5.2.0",
+        "wrap-ansi": "^5.1.0"
+      }
+    },
+    "node_modules/codemirror": {
+      "version": "5.61.1",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.1.tgz",
+      "integrity": "sha512-+D1NZjAucuzE93vJGbAaXzvoBHwp9nJZWWWF9utjv25+5AZUiah6CIlfb4ikG4MoDsFsCG8niiJH5++OO2LgIQ=="
+    },
+    "node_modules/color-convert": {
+      "version": "1.9.3",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
+      "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
+      "dev": true,
+      "dependencies": {
+        "color-name": "1.1.3"
+      }
+    },
+    "node_modules/color-name": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
+      "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=",
+      "dev": true
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "nice-try": "^1.0.4",
+        "path-key": "^2.0.1",
+        "semver": "^5.5.0",
+        "shebang-command": "^1.2.0",
+        "which": "^1.2.9"
+      },
+      "engines": {
+        "node": ">=4.8"
+      }
+    },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/define-properties": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
+      "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
+      "dev": true,
+      "dependencies": {
+        "object-keys": "^1.0.12"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "node_modules/dropzone": {
+      "version": "5.9.2",
+      "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.9.2.tgz",
+      "integrity": "sha512-5t2z51DzIsWDbTpwcJIvUlwxBbvcwdCApz0yb9ecKJwG155Xm92KMEZmHW1B0MzoXOKvFwdd0nPu5cpeVcvPHQ=="
+    },
+    "node_modules/emoji-regex": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
+      "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==",
+      "dev": true
+    },
+    "node_modules/entities": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+      "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==",
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/error-ex": {
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
+      "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
+      "dev": true,
+      "dependencies": {
+        "is-arrayish": "^0.2.1"
+      }
+    },
+    "node_modules/es-abstract": {
+      "version": "1.17.6",
+      "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
+      "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
+      "dev": true,
+      "dependencies": {
+        "es-to-primitive": "^1.2.1",
+        "function-bind": "^1.1.1",
+        "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"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/es-to-primitive": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+      "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+      "dev": true,
+      "dependencies": {
+        "is-callable": "^1.1.4",
+        "is-date-object": "^1.0.1",
+        "is-symbol": "^1.0.2"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.12.8",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.8.tgz",
+      "integrity": "sha512-sx/LwlP/SWTGsd9G4RlOPrXnIihAJ2xwBUmzoqe2nWwbXORMQWtAGNJNYLBJJqa3e9PWvVzxdrtyFZJcr7D87g==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.8.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "to-regex-range": "^5.0.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "locate-path": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "node_modules/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,
+      "engines": {
+        "node": "6.* || 8.* || >= 10.*"
+      }
+    },
+    "node_modules/glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "dependencies": {
+        "is-glob": "^4.0.1"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/has-flag": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+      "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/hosted-git-info": {
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
+      "dev": true
+    },
+    "node_modules/is-arrayish": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+      "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
+      "dev": true
+    },
+    "node_modules/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,
+      "dependencies": {
+        "binary-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/is-callable": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz",
+      "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/is-date-object": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
+      "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "is-extglob": "^2.1.1"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.12.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "has-symbols": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "has-symbols": "^1.0.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+      "dev": true
+    },
+    "node_modules/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
+    },
+    "node_modules/linkify-it": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz",
+      "integrity": "sha512-gDBO4aHNZS6coiZCKVhSNh43F9ioIL4JwRjLZPkoLIY4yZFwg264Y5lu2x6rb1Js42Gh6Yqm2f6L2AJcnkzinQ==",
+      "dependencies": {
+        "uc.micro": "^1.0.1"
+      }
+    },
+    "node_modules/livereload": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.9.3.tgz",
+      "integrity": "sha512-q7Z71n3i4X0R9xthAryBdNGVGAO2R5X+/xXpmKeuPMrteg+W2U8VusTKV3YiJbXZwKsOlFlHe+go6uSNjfxrZw==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": "^3.5.0",
+        "livereload-js": "^3.3.1",
+        "opts": ">= 1.2.0",
+        "ws": "^7.4.3"
+      },
+      "bin": {
+        "livereload": "bin/livereload.js"
+      },
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/livereload-js": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/livereload-js/-/livereload-js-3.3.2.tgz",
+      "integrity": "sha512-w677WnINxFkuixAoUEXOStewzLYGI76XVag+0JWMMEyjJQKs0ibWZMxkTlB96Lm3EjZ7IeOxVziBEbtxVQqQZA==",
+      "dev": true
+    },
+    "node_modules/livereload/node_modules/chokidar": {
+      "version": "3.5.1",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz",
+      "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==",
+      "dev": true,
+      "dependencies": {
+        "anymatch": "~3.1.1",
+        "braces": "~3.0.2",
+        "glob-parent": "~5.1.0",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.5.0"
+      },
+      "engines": {
+        "node": ">= 8.10.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.1"
+      }
+    },
+    "node_modules/livereload/node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/livereload/node_modules/readdirp": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz",
+      "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==",
+      "dev": true,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "graceful-fs": "^4.1.2",
+        "parse-json": "^4.0.0",
+        "pify": "^3.0.0",
+        "strip-bom": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "p-locate": "^3.0.0",
+        "path-exists": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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
+    },
+    "node_modules/markdown-it": {
+      "version": "12.0.6",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.0.6.tgz",
+      "integrity": "sha512-qv3sVLl4lMT96LLtR7xeRJX11OUFjsaD5oVat2/SNBIb21bJXwal2+SklcRbTwGwqWpWH/HRtYavOoJE+seL8w==",
+      "dependencies": {
+        "argparse": "^2.0.1",
+        "entities": "~2.1.0",
+        "linkify-it": "^3.0.1",
+        "mdurl": "^1.0.1",
+        "uc.micro": "^1.0.5"
+      },
+      "bin": {
+        "markdown-it": "bin/markdown-it.js"
+      }
+    },
+    "node_modules/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=="
+    },
+    "node_modules/mdurl": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz",
+      "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4="
+    },
+    "node_modules/memorystream": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz",
+      "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.10.0"
+      }
+    },
+    "node_modules/minimatch": {
+      "version": "3.0.4",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+      "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+      "dev": true,
+      "dependencies": {
+        "brace-expansion": "^1.1.7"
+      },
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/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_modules/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,
+      "dependencies": {
+        "hosted-git-info": "^2.1.4",
+        "resolve": "^1.10.0",
+        "semver": "2 || 3 || 4 || 5",
+        "validate-npm-package-license": "^3.0.1"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "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"
+      },
+      "bin": {
+        "npm-run-all": "bin/npm-run-all/index.js",
+        "run-p": "bin/run-p/index.js",
+        "run-s": "bin/run-s/index.js"
+      },
+      "engines": {
+        "node": ">= 4"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "define-properties": "^1.1.2",
+        "function-bind": "^1.1.1",
+        "has-symbols": "^1.0.0",
+        "object-keys": "^1.0.11"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/opts": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/opts/-/opts-2.0.2.tgz",
+      "integrity": "sha512-k41FwbcLnlgnFh69f4qdUfvDQ+5vaSDnVPFI/y5XuhKRq97EnVVneO9F1ESVCdiVu4fCS2L8usX3mU331hB7pg==",
+      "dev": true
+    },
+    "node_modules/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,
+      "dependencies": {
+        "p-try": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "p-limit": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "error-ex": "^1.3.1",
+        "json-parse-better-errors": "^1.0.1"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/path-key": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
+      "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "pify": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/picomatch": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz",
+      "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.6"
+      }
+    },
+    "node_modules/pidtree": {
+      "version": "0.3.1",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz",
+      "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==",
+      "dev": true,
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/pify": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
+      "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "load-json-file": "^4.0.0",
+        "normalize-package-data": "^2.3.2",
+        "path-type": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "picomatch": "^2.2.1"
+      },
+      "engines": {
+        "node": ">=8.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/resolve": {
+      "version": "1.17.0",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+      "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+      "dev": true,
+      "dependencies": {
+        "path-parse": "^1.0.6"
+      }
+    },
+    "node_modules/sass": {
+      "version": "1.34.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.34.1.tgz",
+      "integrity": "sha512-scLA7EIZM+MmYlej6sdVr0HRbZX5caX5ofDT9asWnUJj21oqgsC+1LuNfm0eg+vM0fCTZHhwImTiCU0sx9h9CQ==",
+      "dev": true,
+      "dependencies": {
+        "chokidar": ">=3.0.0 <4.0.0"
+      },
+      "bin": {
+        "sass": "sass.js"
+      },
+      "engines": {
+        "node": ">=8.9.0"
+      }
+    },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0="
+    },
+    "node_modules/semver": {
+      "version": "5.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+      "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "shebang-regex": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/sortablejs": {
+      "version": "1.13.0",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.13.0.tgz",
+      "integrity": "sha512-RBJirPY0spWCrU5yCmWM1eFs/XgX2J5c6b275/YyxFRgnzPhKl/TDeU2hNR8Dt7ITq66NRPM4UlOt+e5O4CFHg=="
+    },
+    "node_modules/spdx-correct": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
+      "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
+      "dev": true,
+      "dependencies": {
+        "spdx-expression-parse": "^3.0.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "spdx-exceptions": "^2.1.0",
+        "spdx-license-ids": "^3.0.0"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "emoji-regex": "^7.0.1",
+        "is-fullwidth-code-point": "^2.0.0",
+        "strip-ansi": "^5.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.0-next.1"
+      },
+      "engines": {
+        "node": ">= 0.4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "define-properties": "^1.1.3",
+        "es-abstract": "^1.17.5"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "ansi-regex": "^4.1.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/strip-bom": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+      "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
+      "dev": true,
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "has-flag": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=4"
+      }
+    },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "node_modules/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,
+      "dependencies": {
+        "is-number": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=8.0"
+      }
+    },
+    "node_modules/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=="
+    },
+    "node_modules/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,
+      "dependencies": {
+        "spdx-correct": "^3.0.0",
+        "spdx-expression-parse": "^3.0.0"
+      }
+    },
+    "node_modules/which": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+      "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+      "dev": true,
+      "dependencies": {
+        "isexe": "^2.0.0"
+      },
+      "bin": {
+        "which": "bin/which"
+      }
+    },
+    "node_modules/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
+    },
+    "node_modules/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,
+      "dependencies": {
+        "ansi-styles": "^3.2.0",
+        "string-width": "^3.0.0",
+        "strip-ansi": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=6"
+      }
+    },
+    "node_modules/ws": {
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+      "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+      "dev": true,
+      "engines": {
+        "node": ">=8.3.0"
+      },
+      "peerDependencies": {
+        "bufferutil": "^4.0.1",
+        "utf-8-validate": "^5.0.2"
+      },
+      "peerDependenciesMeta": {
+        "bufferutil": {
+          "optional": true
+        },
+        "utf-8-validate": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/y18n": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz",
+      "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==",
+      "dev": true
+    },
+    "node_modules/yargs": {
+      "version": "13.3.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz",
+      "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^5.0.0",
+        "find-up": "^3.0.0",
+        "get-caller-file": "^2.0.1",
+        "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.2"
+      }
+    },
+    "node_modules/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,
+      "dependencies": {
+        "camelcase": "^5.0.0",
+        "decamelize": "^1.2.0"
+      }
+    }
+  },
   "dependencies": {
     "ansi-regex": {
       "version": "4.1.0",
       }
     },
     "argparse": {
-      "version": "1.0.10",
-      "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
-      "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
-      "requires": {
-        "sprintf-js": "~1.0.2"
-      }
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
     },
     "balanced-match": {
       "version": "1.0.0",
       }
     },
     "codemirror": {
-      "version": "5.60.0",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.60.0.tgz",
-      "integrity": "sha512-AEL7LhFOlxPlCL8IdTcJDblJm8yrAGib7I+DErJPdZd4l6imx8IMgKK3RblVgBQqz3TZJR4oknQ03bz+uNjBYA=="
+      "version": "5.61.1",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.61.1.tgz",
+      "integrity": "sha512-+D1NZjAucuzE93vJGbAaXzvoBHwp9nJZWWWF9utjv25+5AZUiah6CIlfb4ikG4MoDsFsCG8niiJH5++OO2LgIQ=="
     },
     "color-convert": {
       "version": "1.9.3",
       "dev": true
     },
     "entities": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
-      "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz",
+      "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w=="
     },
     "error-ex": {
       "version": "1.3.2",
       }
     },
     "esbuild": {
-      "version": "0.7.8",
-      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.7.8.tgz",
-      "integrity": "sha512-6UT1nZB+8ja5avctUC6d3kGOUAhy6/ZYHljL4nk3++1ipadghBhUCAcwsTHsmUvdu04CcGKzo13mE+ZQ2O3zrA==",
+      "version": "0.12.8",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.12.8.tgz",
+      "integrity": "sha512-sx/LwlP/SWTGsd9G4RlOPrXnIihAJ2xwBUmzoqe2nWwbXORMQWtAGNJNYLBJJqa3e9PWvVzxdrtyFZJcr7D87g==",
       "dev": true
     },
     "escape-string-regexp": {
       "dev": true
     },
     "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==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
       "dev": true,
       "requires": {
         "is-glob": "^4.0.1"
       "dev": true
     },
     "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==",
+      "version": "2.8.9",
+      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
+      "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
       "dev": true
     },
     "is-arrayish": {
       "dev": true
     },
     "markdown-it": {
-      "version": "11.0.1",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz",
-      "integrity": "sha512-aU1TzmBKcWNNYvH9pjq6u92BML+Hz3h5S/QpfTFwiQF852pLT+9qHsrhM9JYipkOXZxGn+sGH8oyJE9FD9WezQ==",
+      "version": "12.0.6",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.0.6.tgz",
+      "integrity": "sha512-qv3sVLl4lMT96LLtR7xeRJX11OUFjsaD5oVat2/SNBIb21bJXwal2+SklcRbTwGwqWpWH/HRtYavOoJE+seL8w==",
       "requires": {
-        "argparse": "^1.0.7",
-        "entities": "~2.0.0",
+        "argparse": "^2.0.1",
+        "entities": "~2.1.0",
         "linkify-it": "^3.0.1",
         "mdurl": "^1.0.1",
         "uc.micro": "^1.0.5"
       }
     },
     "sass": {
-      "version": "1.32.8",
-      "resolved": "https://registry.npmjs.org/sass/-/sass-1.32.8.tgz",
-      "integrity": "sha512-Sl6mIeGpzjIUZqvKnKETfMf0iDAswD9TNlv13A7aAF3XZlRPMq4VvJWBC2N2DXbp94MQVdNSFG6LfF/iOXrPHQ==",
+      "version": "1.34.1",
+      "resolved": "https://registry.npmjs.org/sass/-/sass-1.34.1.tgz",
+      "integrity": "sha512-scLA7EIZM+MmYlej6sdVr0HRbZX5caX5ofDT9asWnUJj21oqgsC+1LuNfm0eg+vM0fCTZHhwImTiCU0sx9h9CQ==",
       "dev": true,
       "requires": {
-        "chokidar": ">=2.0.0 <4.0.0"
+        "chokidar": ">=3.0.0 <4.0.0"
       }
     },
     "select": {
       "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==",
       "dev": true
     },
-    "sprintf-js": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
-      "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
-    },
     "string-width": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
       }
     },
     "ws": {
-      "version": "7.4.4",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.4.tgz",
-      "integrity": "sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==",
-      "dev": true
+      "version": "7.4.6",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
+      "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
+      "dev": true,
+      "requires": {}
     },
     "y18n": {
       "version": "4.0.1",
index 503abd44fda5b9a12b15ca3987aee96367757f5e..f4b7bf5dc6f0358a3fc895e62999c1ee7aa08ab0 100644 (file)
   },
   "devDependencies": {
     "chokidar-cli": "^2.1.0",
-    "esbuild": "0.7.8",
+    "esbuild": "0.12.8",
     "livereload": "^0.9.3",
     "npm-run-all": "^4.1.5",
     "punycode": "^2.1.1",
-    "sass": "^1.32.8"
+    "sass": "^1.34.1"
   },
   "dependencies": {
     "clipboard": "^2.0.8",
-    "codemirror": "^5.60.0",
+    "codemirror": "^5.61.1",
     "dropzone": "^5.9.2",
-    "markdown-it": "^11.0.1",
+    "markdown-it": "^12.0.6",
     "markdown-it-task-lists": "^2.1.1",
     "sortablejs": "^1.13.0"
   }
index 0e3f1687911a6316e4b7de75c05d85c16770659a..a7667e48f1a4c68e1168a0567912bf2279bac52f 100644 (file)
@@ -1,4 +1,3 @@
 <svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-    <path d="M0 0h24v24H0z" fill="none"/>
     <path d="M11.99 2C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zm4.24 16L12 15.45 7.77 18l1.12-4.81-3.73-3.23 4.92-.42L12 5l1.92 4.53 4.92.42-3.73 3.23L16.23 18z"/>
 </svg>
\ No newline at end of file
diff --git a/resources/icons/star-outline.svg b/resources/icons/star-outline.svg
new file mode 100644 (file)
index 0000000..4e83ab4
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M22 9.24l-7.19-.62L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21 12 17.27 18.18 21l-1.63-7.03L22 9.24zM12 15.4l-3.76 2.27 1-4.28-3.32-2.88 4.38-.38L12 6.1l1.71 4.04 4.38.38-3.32 2.88 1 4.28L12 15.4z"/></svg>
\ No newline at end of file
index 73e328cee060851823187396a8bccc2aee41bcc0..1d92d2b1849ec6d9ea1d28f904c8644d0e2de055 100644 (file)
@@ -1,4 +1 @@
-<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
-    <path d="M0 0h24v24H0z" fill="none"/>
-    <path d="M17.63 5.84C17.27 5.33 16.67 5 16 5L5 5.01C3.9 5.01 3 5.9 3 7v10c0 1.1.9 1.99 2 1.99L16 19c.67 0 1.27-.33 1.63-.84L22 12l-4.37-6.16z"/>
-</svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" viewBox="0 0 24 24"><path d="M21.41,11.41l-8.83-8.83C12.21,2.21,11.7,2,11.17,2H4C2.9,2,2,2.9,2,4v7.17c0,0.53,0.21,1.04,0.59,1.41l8.83,8.83 c0.78,0.78,2.05,0.78,2.83,0l7.17-7.17C22.2,13.46,22.2,12.2,21.41,11.41z M6.5,8C5.67,8,5,7.33,5,6.5S5.67,5,6.5,5S8,5.67,8,6.5 S7.33,8,6.5,8z"/></svg>
\ No newline at end of file
diff --git a/resources/js/components/attachments-list.js b/resources/js/components/attachments-list.js
new file mode 100644 (file)
index 0000000..34979c2
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * Attachments List
+ * Adds '?open=true' query to file attachment links
+ * when ctrl/cmd is pressed down.
+ * @extends {Component}
+ */
+class AttachmentsList {
+
+    setup() {
+        this.container = this.$el;
+        this.setupListeners();
+    }
+
+    setupListeners() {
+        const isExpectedKey = (event) => event.key === 'Control' || event.key === 'Meta';
+        window.addEventListener('keydown', event => {
+             if (isExpectedKey(event)) {
+                this.addOpenQueryToLinks();
+             }
+        }, {passive: true});
+        window.addEventListener('keyup', event => {
+            if (isExpectedKey(event)) {
+                this.removeOpenQueryFromLinks();
+            }
+        }, {passive: true});
+    }
+
+    addOpenQueryToLinks() {
+        const links = this.container.querySelectorAll('a.attachment-file');
+        for (const link of links) {
+            if (link.href.split('?')[1] !== 'open=true') {
+                link.href = link.href + '?open=true';
+                link.setAttribute('target', '_blank');
+            }
+        }
+    }
+
+    removeOpenQueryFromLinks() {
+        const links = this.container.querySelectorAll('a.attachment-file');
+        for (const link of links) {
+            link.href = link.href.split('?')[0];
+            link.removeAttribute('target');
+        }
+    }
+}
+
+export default AttachmentsList;
\ No newline at end of file
index 22402d483902b2148918150e1d371611dd1abf9b..f761ecf011541590963caf66daf4b145f43c8cde 100644 (file)
@@ -12,6 +12,7 @@ class DropDown {
         this.menu = this.$refs.menu;
         this.toggle = this.$refs.toggle;
         this.moveMenu = this.$opts.moveMenu;
+        this.bubbleEscapes = this.$opts.bubbleEscapes === 'true';
 
         this.direction = (document.dir === 'rtl') ? 'right' : 'left';
         this.body = document.body;
@@ -137,7 +138,9 @@ class DropDown {
             } else if (event.key === 'Escape') {
                 this.hide();
                 this.toggle.focus();
-                event.stopPropagation();
+                if (!this.bubbleEscapes) {
+                    event.stopPropagation();
+                }
             }
         };
         this.container.addEventListener('keydown', keyboardNavigation);
index eccd4b8f0a6b25e722b1754cd6c611db1ed3ac64..99737bfb8b0fb741564a5c854d566fd3f774067a 100644 (file)
@@ -1,31 +1,41 @@
 
 class HeaderMobileToggle {
 
-    constructor(elem) {
-        this.elem = elem;
-        this.toggleButton = elem.querySelector('.mobile-menu-toggle');
-        this.menu = elem.querySelector('.header-links');
-        this.open = false;
+    setup() {
+        this.elem = this.$el;
+        this.toggleButton = this.$refs.toggle;
+        this.menu = this.$refs.menu;
 
+        this.open = false;
         this.toggleButton.addEventListener('click', this.onToggle.bind(this));
         this.onWindowClick = this.onWindowClick.bind(this);
+        this.onKeyDown = this.onKeyDown.bind(this);
     }
 
     onToggle(event) {
         this.open = !this.open;
         this.menu.classList.toggle('show', this.open);
+        this.toggleButton.setAttribute('aria-expanded', this.open ? 'true' : 'false');
         if (this.open) {
+            this.elem.addEventListener('keydown', this.onKeyDown);
             window.addEventListener('click', this.onWindowClick)
         } else {
+            this.elem.removeEventListener('keydown', this.onKeyDown);
             window.removeEventListener('click', this.onWindowClick)
         }
         event.stopPropagation();
     }
 
+    onKeyDown(event) {
+        if (event.code === 'Escape') {
+            this.onToggle(event);
+        }
+    }
+
     onWindowClick(event) {
         this.onToggle(event);
     }
 
 }
 
-module.exports = HeaderMobileToggle;
\ No newline at end of file
+export default HeaderMobileToggle;
\ No newline at end of file
index 91ccdaf3aa6f23fca3a923283634c75eb42a0265..010ee04bae01f349a95ccf9d0eb8dac3f86ce051 100644 (file)
@@ -2,6 +2,7 @@ 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 attachmentsList from "./attachments-list.js"
 import autoSuggest from "./auto-suggest.js"
 import backToTop from "./back-to-top.js"
 import bookSort from "./book-sort.js"
@@ -56,6 +57,7 @@ const componentMapping = {
     "ajax-delete-row": ajaxDeleteRow,
     "ajax-form": ajaxForm,
     "attachments": attachments,
+    "attachments-list": attachmentsList,
     "auto-suggest": autoSuggest,
     "back-to-top": backToTop,
     "book-sort": bookSort,
index 78581ec447f5cf099d64d681c976295d3c6875af..a90f74e2746401562f59c03cea0b8824298f9dc8 100644 (file)
@@ -14,6 +14,7 @@ class MarkdownEditor {
         this.pageId = this.$opts.pageId;
         this.textDirection = this.$opts.textDirection;
         this.imageUploadErrorText = this.$opts.imageUploadErrorText;
+        this.serverUploadLimitText = this.$opts.serverUploadLimitText;
 
         this.markdown = new MarkdownIt({html: true});
         this.markdown.use(mdTasksLists, {label: true});
@@ -446,8 +447,7 @@ class MarkdownEditor {
                 this.insertDrawing(resp.data, cursorPos);
                 DrawIO.close();
             }).catch(err => {
-                window.$events.emit('error', trans('errors.image_upload_error'));
-                console.log(err);
+                this.handleDrawingUploadError(err);
             });
         });
     }
@@ -491,12 +491,20 @@ class MarkdownEditor {
                 this.cm.focus();
                 DrawIO.close();
             }).catch(err => {
-                window.$events.emit('error', this.imageUploadErrorText);
-                console.log(err);
+                this.handleDrawingUploadError(err);
             });
         });
     }
 
+    handleDrawingUploadError(error) {
+        if (error.status === 413) {
+            window.$events.emit('error', this.serverUploadLimitText);
+        } else {
+            window.$events.emit('error', this.imageUploadErrorText);
+        }
+        console.log(error);
+    }
+
     // Make the editor full screen
     actionFullScreen() {
         const alreadyFullscreen = this.elem.classList.contains('fullscreen');
index 905ca03b1020d566859366d6e2ecc8e851edc784..f801e52a193715fdea427fa4eac03c2372ca9abf 100644 (file)
@@ -1,8 +1,9 @@
 
 class TriLayout {
 
-    constructor(elem) {
-        this.elem = elem;
+    setup() {
+        this.container = this.$refs.container;
+        this.tabs = this.$manyRefs.tab;
 
         this.lastLayoutType = 'none';
         this.onDestroy = null;
@@ -43,13 +44,12 @@ class TriLayout {
     }
 
     setupMobile() {
-        const layoutTabs = document.querySelectorAll('[tri-layout-mobile-tab]');
-        for (let tab of layoutTabs) {
+        for (const tab of this.tabs) {
             tab.addEventListener('click', this.mobileTabClick);
         }
 
         this.onDestroy = () => {
-            for (let tab of layoutTabs) {
+            for (const tab of this.tabs) {
                 tab.removeEventListener('click', this.mobileTabClick);
             }
         }
@@ -65,7 +65,7 @@ class TriLayout {
      * @param event
      */
     mobileTabClick(event) {
-        const tab = event.target.getAttribute('tri-layout-mobile-tab');
+        const tab = event.target.dataset.tab;
         this.showTab(tab);
     }
 
@@ -79,21 +79,21 @@ class TriLayout {
 
     /**
      * Show the given tab
-     * @param tabName
+     * @param {String} tabName
+     * @param {Boolean }scroll
      */
     showTab(tabName, scroll = true) {
         this.scrollCache[this.lastTabShown] = document.documentElement.scrollTop;
 
         // Set tab status
-        const tabs = document.querySelectorAll('.tri-layout-mobile-tab');
-        for (let tab of tabs) {
-            const isActive = (tab.getAttribute('tri-layout-mobile-tab') === tabName);
-            tab.classList.toggle('active', isActive);
+        for (const tab of this.tabs) {
+            const isActive = (tab.dataset.tab === tabName);
+            tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
         }
 
         // Toggle section
         const showInfo = (tabName === 'info');
-        this.elem.classList.toggle('show-info', showInfo);
+        this.container.classList.toggle('show-info', showInfo);
 
         // Set the scroll position from cache
         if (scroll) {
index a6ab542181b9c20558bf0f030b98254181fc804c..bde73f4bfb12aa2e87e4f3d250e7b76d8831e314 100644 (file)
@@ -152,8 +152,8 @@ function codePlugin() {
             return;
         }
 
-        let lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
-        let currentCode = selectedNode.querySelector('textarea').textContent;
+        const lang = selectedNode.hasAttribute('data-lang') ? selectedNode.getAttribute('data-lang') : '';
+        const currentCode = selectedNode.querySelector('textarea').textContent;
 
         window.components.first('code-editor').open(currentCode, lang, (code, lang) => {
             const editorElem = selectedNode.querySelector('.CodeMirror');
@@ -225,22 +225,23 @@ function codePlugin() {
                 return elem.contentEditable !== "false";
             });
 
-            if (!codeSamples.length) return;
-            editor.undoManager.transact(function () {
-                codeSamples.each((index, elem) => {
-                    Code.wysiwygView(elem);
-                });
+            codeSamples.each((index, elem) => {
+                Code.wysiwygView(elem);
             });
         }
 
         editor.on('init', function() {
             // Parse code mirror instances on init, but delay a little so this runs after
             // initial styles are fetched into the editor.
-            parseCodeMirrorInstances();
+            editor.undoManager.transact(function () {
+                parseCodeMirrorInstances();
+            });
             // Parsed code mirror blocks when content is set but wait before setting this handler
             // to avoid any init 'SetContent' events.
             setTimeout(() => {
-                editor.on('SetContent', parseCodeMirrorInstances);
+                editor.on('SetContent', () => {
+                    setTimeout(parseCodeMirrorInstances, 100);
+                });
             }, 200);
         });
 
@@ -282,6 +283,15 @@ function drawIoPlugin(drawioUrl, isDarkMode, pageId, wysiwygComponent) {
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
 
+        const handleUploadError = (error) => {
+            if (error.status === 413) {
+                window.$events.emit('error', wysiwygComponent.serverUploadLimitText);
+            } else {
+                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
+            }
+            console.log(error);
+        };
+
         // Handle updating an existing image
         if (currentNode) {
             DrawIO.close();
@@ -291,8 +301,7 @@ function drawIoPlugin(drawioUrl, isDarkMode, pageId, wysiwygComponent) {
                 pageEditor.dom.setAttrib(imgElem, 'src', img.url);
                 pageEditor.dom.setAttrib(currentNode, 'drawio-diagram', img.id);
             } catch (err) {
-                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
-                console.log(err);
+                handleUploadError(err);
             }
             return;
         }
@@ -306,8 +315,7 @@ function drawIoPlugin(drawioUrl, isDarkMode, pageId, wysiwygComponent) {
                 pageEditor.dom.get(id).parentNode.setAttribute('drawio-diagram', img.id);
             } catch (err) {
                 pageEditor.dom.remove(id);
-                window.$events.emit('error', wysiwygComponent.imageUploadErrorText);
-                console.log(err);
+                handleUploadError(err);
             }
         }, 5);
     }
@@ -431,6 +439,7 @@ class WysiwygEditor {
         this.pageId = this.$opts.pageId;
         this.textDirection = this.$opts.textDirection;
         this.imageUploadErrorText = this.$opts.imageUploadErrorText;
+        this.serverUploadLimitText = this.$opts.serverUploadLimitText;
         this.isDarkMode = document.documentElement.classList.contains('dark-mode');
 
         this.plugins = "image imagetools table textcolor paste link autolink fullscreen code customhr autosave lists codeeditor media";
index 17e57cd6b9165d567aa09f48197ea95e8b62551f..6e22919fb4d230556d99b6ce6a4382f077866af4 100644 (file)
@@ -1,5 +1,5 @@
 let iFrame = null;
-
+let lastApprovedOrigin;
 let onInit, onSave;
 
 /**
@@ -19,15 +19,22 @@ function show(drawioUrl, onInitCallback, onSaveCallback) {
     iFrame.setAttribute('class', 'fullscreen');
     iFrame.style.backgroundColor = '#FFFFFF';
     document.body.appendChild(iFrame);
+    lastApprovedOrigin = (new URL(drawioUrl)).origin;
 }
 
 function close() {
     drawEventClose();
 }
 
+/**
+ * Receive and handle a message event from the draw.io window.
+ * @param {MessageEvent} event
+ */
 function drawReceive(event) {
     if (!event.data || event.data.length < 1) return;
-    let message = JSON.parse(event.data);
+    if (event.origin !== lastApprovedOrigin) return;
+
+    const message = JSON.parse(event.data);
     if (message.event === 'init') {
         drawEventInit();
     } else if (message.event === 'exit') {
@@ -62,7 +69,7 @@ function drawEventClose() {
 }
 
 function drawPostMessage(data) {
-    iFrame.contentWindow.postMessage(JSON.stringify(data), '*');
+    iFrame.contentWindow.postMessage(JSON.stringify(data), lastApprovedOrigin);
 }
 
 async function upload(imageData, pageUploadedToId) {
index b05dd23bfd7222834eade6395f655d5916b1b2c1..00865e69bd8ff83698864ed7c6d0dd0042ef465f 100644 (file)
@@ -149,8 +149,8 @@ async function getResponseContent(response) {
         return null;
     }
 
-    const responseContentType = response.headers.get('Content-Type');
-    const subType = responseContentType.split('/').pop();
+    const responseContentType = response.headers.get('Content-Type') || '';
+    const subType = responseContentType.split(';')[0].split('/').pop();
 
     if (subType === 'javascript' || subType === 'json') {
         return await response.json();
index 6dc8a83978dcc9e5b8f2c446fd368c68cf9f0722..05055d1bccbc6aa9442c87228506e76e1b384e60 100644 (file)
@@ -33,7 +33,7 @@ return [
     'book_delete'                 => 'تم حذف الكتاب',
     'book_delete_notification'    => 'تم حذف الكتاب بنجاح',
     'book_sort'                   => 'تم سرد الكتاب',
-    'book_sort_notification'      => 'تÙ\85ت Ø¥Ø¹Ø§Ø¯Ø© سرد الكتاب بنجاح',
+    'book_sort_notification'      => 'Ø£Ù\8fعÙ\90Ù\8aدÙ\8e سرد الكتاب بنجاح',
 
     // Bookshelves
     'bookshelf_create'            => 'تم إنشاء رف الكتب',
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'تم تحديث الرف',
     'bookshelf_delete_notification'    => 'تم حذف الرف بنجاح',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'تم التعليق',
     'permissions_update'          => 'تحديث الأذونات',
index 60a7af4114a4c6c43f2302f97f15c199092f98c5..363310bba914b78e54ea4b2da260ec3457d801fe 100644 (file)
@@ -47,7 +47,7 @@ return [
     'reset_password_success' => 'تمت استعادة كلمة المرور بنجاح.',
     'email_reset_subject' => 'استعد كلمة المرور الخاصة بتطبيق :appName',
     'email_reset_text' => 'تم إرسال هذه الرسالة بسبب تلقينا لطلب استعادة كلمة المرور الخاصة بحسابكم.',
-    'email_reset_not_requested' => 'إذا لم يتم طلب استعادة كلمة المرور من قبلكم, فلا حاجة لاتخاذ أية خطوات.',
+    'email_reset_not_requested' => 'إذا لم يتم طلب استعادة كلمة المرور من قبلكم، فلا حاجة لاتخاذ أية خطوات.',
 
 
     // Email Confirmation
@@ -62,11 +62,11 @@ return [
     'email_not_confirmed' => 'لم يتم تأكيد البريد الإلكتروني',
     'email_not_confirmed_text' => 'لم يتم بعد تأكيد عنوان البريد الإلكتروني.',
     'email_not_confirmed_click_link' => 'الرجاء الضغط على الرابط المرسل إلى بريدكم الإلكتروني بعد تسجيلكم.',
-    'email_not_confirmed_resend' => 'إذا لم يتم إيجاد الرسالة, بإمكانكم إعادة إرسال رسالة التأكيد عن طريق تعبئة النموذج أدناه.',
+    'email_not_confirmed_resend' => 'إذا لم يتم إيجاد الرسالة، بإمكانكم إعادة إرسال رسالة التأكيد عن طريق تعبئة النموذج أدناه.',
     'email_not_confirmed_resend_button' => 'إعادة إرسال رسالة التأكيد',
 
     // User Invite
-    'user_invite_email_subject' => 'تم دعوتك للإنضمام إلى صفحة الحالة الخاصة بـ :app_name!',
+    'user_invite_email_subject' => 'تمت دعوتك للانضمام إلى صفحة الحالة الخاصة بـ :app_name!',
     'user_invite_email_greeting' => 'تم إنشاء حساب مستخدم لك على %site%.',
     'user_invite_email_text' => 'انقر على الزر أدناه لتعيين كلمة مرور الحساب والحصول على الوصول:',
     'user_invite_email_action' => 'كلمة سر المستخدم',
index 2b5ed4d8d10cbbece9dd32de0a63cc2a51c1cbf4..b9b2032344683ff104edc4652f3bd118651e38b7 100644 (file)
@@ -40,22 +40,26 @@ return [
     'remove' => 'إزالة',
     'add' => 'إضافة',
     'fullscreen' => 'شاشة كاملة',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
-    'sort_options' => 'خيارات الترتيب',
-    'sort_direction_toggle' => 'الترتيب وفق الإتجاه',
+    'sort_options' => 'خيارات الفرز',
+    'sort_direction_toggle' => 'الفرز وفق الاتجاه',
     'sort_ascending' => 'فرز تصاعدي',
     'sort_descending' => 'فرز تنازلي',
     'sort_name' => 'الاسم',
-    'sort_default' => 'Default',
+    'sort_default' => 'افتراضي',
     'sort_created_at' => 'تاريخ الإنشاء',
     'sort_updated_at' => 'تاريخ التحديث',
 
     // Misc
-    'deleted_user' => 'حذÙ\81 Ù\85ستخدÙ\85',
+    'deleted_user' => 'اÙ\84Ù\85ستخدÙ\85 Ø§Ù\84Ù\85حذÙ\88Ù\81',
     'no_activity' => 'لا يوجد نشاط لعرضه',
     'no_items' => 'لا توجد عناصر متوفرة',
-    'back_to_top' => 'العودة للبداية',
+    'back_to_top' => 'العودة إلى الأعلى',
     'toggle_details' => 'عرض / إخفاء التفاصيل',
     'toggle_thumbnails' => 'عرض / إخفاء الصور المصغرة',
     'details' => 'التفاصيل',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'شريط التنقل',
 
     // Header
+    'header_menu_expand' => 'عرض القائمة',
     'profile_menu' => 'قائمة ملف التعريف',
     'view_profile' => 'عرض الملف الشخصي',
     'edit_profile' => 'تعديل الملف الشخصي',
@@ -73,14 +78,16 @@ return [
 
     // Layout tabs
     'tab_info' => 'معلومات',
+    'tab_info_label' => 'تبويب: إظهار المعلومات الثانوية',
     'tab_content' => 'المحتوى',
+    'tab_content_label' => 'تبويب: إظهار المحتوى الأساسي',
 
     // Email Content
-    'email_action_help' => 'إذا Ù\88اجÙ\87تÙ\83Ù\85 Ù\85Ø´Ù\83Ù\84Ø© Ø¨ضغط زر ":actionText" فبإمكانكم نسخ الرابط أدناه ولصقه بالمتصفح:',
+    'email_action_help' => 'إذا Ù\88اجÙ\87تÙ\83Ù\85 Ù\85Ø´Ù\83Ù\84Ø© Ø¹Ù\86د ضغط زر ":actionText" فبإمكانكم نسخ الرابط أدناه ولصقه بالمتصفح:',
     'email_rights' => 'جميع الحقوق محفوظة',
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'سياسة الخصوصية',
+    'terms_of_service' => 'اتفاقية شروط الخدمة',
 ];
index 422de114b2aba7490926a61b0e77c4de0245da7d..803232b2d00e94706b151da31f4f1647dbebad17 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'المزيد',
     'image_image_name' => 'اسم الصورة',
     'image_delete_used' => 'هذه الصورة مستخدمة بالصفحات أدناه.',
-    'image_delete_confirm_text' => 'هل أنت متأكد من أنك تريد حذف هذه الصورة ؟',
+    'image_delete_confirm_text' => 'هل أنت متأكد من أنك تريد حذف هذه الصورة؟',
     'image_select_image' => 'تحديد الصورة',
     'image_dropzone' => 'قم بإسقاط الصورة أو اضغط هنا للرفع',
     'images_deleted' => 'تم حذف الصور',
index 8771f64eec9435c9a44066cd21be6323e87c119d..dec4522f43bffc28e94a103f97ad98107c95f6f5 100644 (file)
@@ -11,7 +11,7 @@ return [
     'recently_updated_pages' => 'صفحات حُدثت مؤخراً',
     'recently_created_chapters' => 'فصول أنشئت مؤخراً',
     'recently_created_books' => 'كتب أنشئت مؤخراً',
-    'recently_created_shelves' => 'اÙ\84أرÙ\81Ù\81 Ø§Ù\84Ù\85Ù\86شأة مؤخراً',
+    'recently_created_shelves' => 'أرÙ\81Ù\81 Ø£Ù\86شئت مؤخراً',
     'recently_update' => 'حُدثت مؤخراً',
     'recently_viewed' => 'عُرضت مؤخراً',
     'recent_activity' => 'نشاطات حديثة',
@@ -27,9 +27,11 @@ return [
     'images' => 'صور',
     'my_recent_drafts' => 'مسوداتي الحديثة',
     'my_recently_viewed' => 'ما عرضته مؤخراً',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'لم تستعرض أي صفحات',
-    'no_pages_recently_created' => 'لم يتم إنشاء أي صفحات مؤخراً',
-    'no_pages_recently_updated' => 'لم يتم تحديث أي صفحات مؤخراً',
+    'no_pages_recently_created' => 'لم تنشأ أي صفحات مؤخراً',
+    'no_pages_recently_updated' => 'لم تُحدّث أي صفحات مؤخراً',
     'export' => 'تصدير',
     'export_html' => 'صفحة ويب',
     'export_pdf' => 'ملف PDF',
@@ -37,7 +39,7 @@ return [
 
     // Permissions and restrictions
     'permissions' => 'الأذونات',
-    'permissions_intro' => 'في حال التفعيل, ستتم تبدية هذه الأذونات على أذونات الأدوار.',
+    'permissions_intro' => 'عند التفعيل، سوف تأخذ هذه الأذونات أولوية على أي صلاحية أخرى للدور.',
     'permissions_enable' => 'تفعيل الأذونات المخصصة',
     'permissions_save' => 'حفظ الأذونات',
     'permissions_owner' => 'Owner',
@@ -55,8 +57,8 @@ return [
     'search_exact_matches' => 'نتائج مطابقة تماماً',
     'search_tags' => 'بحث الوسوم',
     'search_options' => 'الخيارات',
-    'search_viewed_by_me' => 'تÙ\85 Ø§Ø³ØªØ¹Ø±Ø§Ø¶Ù\87ا من قبلي',
-    'search_not_viewed_by_me' => 'لم يتم استعراضها من قبلي',
+    'search_viewed_by_me' => 'استعرضت من قبلي',
+    'search_not_viewed_by_me' => 'لم تستعرض من قبلي',
     'search_permissions_set' => 'حزمة الأذونات',
     'search_created_by_me' => 'أنشئت بواسطتي',
     'search_updated_by_me' => 'حُدثت بواسطتي',
@@ -74,24 +76,24 @@ return [
     'shelves' => 'الأرفف',
     'x_shelves' => ':count رف|:count أرفف',
     'shelves_long' => 'أرفف الكتب',
-    'shelves_empty' => 'لم يتم إنشاء أي أرفف',
+    'shelves_empty' => 'لم ينشأ أي رف',
     'shelves_create' => 'إنشاء رف جديد',
-    'shelves_popular' => 'أرÙ\81Ù\81 Ø´Ø¹Ø¨Ù\8aة',
+    'shelves_popular' => 'أرÙ\81Ù\81 Ø±Ø§Ø¦Ø¬ة',
     'shelves_new' => 'أرفف جديدة',
     'shelves_new_action' => 'رف جديد',
     'shelves_popular_empty' => 'ستظهر هنا الأرفف الأكثر رواجًا.',
-    'shelves_new_empty' => 'ستظÙ\87ر Ù\87Ù\86ا Ø§Ù\84أرÙ\81Ù\81 Ø§Ù\84تÙ\8a ØªÙ\85 Ø¥Ù\86شاؤÙ\87ا مؤخرًا.',
+    'shelves_new_empty' => 'ستظÙ\87ر Ù\87Ù\86ا Ø§Ù\84أرÙ\81Ù\81 Ø§Ù\84تÙ\8a Ø£Ù\86شئت مؤخرًا.',
     'shelves_save' => 'حفظ الرف',
     'shelves_books' => 'كتب على هذا الرف',
     'shelves_add_books' => 'إضافة كتب لهذا الرف',
-    'shelves_drag_books' => 'اسحب Ø§Ù\84Ù\83تب Ù\87Ù\86ا Ù\84إضاÙ\81تÙ\87ا Ù\84هذا الرف',
+    'shelves_drag_books' => 'اسحب Ø§Ù\84Ù\83تب Ù\87Ù\86ا Ù\84إضاÙ\81تÙ\87ا Ù\81Ù\8a هذا الرف',
     'shelves_empty_contents' => 'لا توجد كتب مخصصة لهذا الرف',
     'shelves_edit_and_assign' => 'تحرير الرف لإدراج كتب',
-    'shelves_edit_named' => 'تحرير رف الكتب: الاسم',
+    'shelves_edit_named' => 'تحرير رف الكتب :name',
     'shelves_edit' => 'تحرير رف الكتب',
     'shelves_delete' => 'حذف رف الكتب',
-    'shelves_delete_named' => 'حذف رف الكتب: الاسم',
-    'shelves_delete_explain' => "سيؤدي هذا إلى حذف رف الكتب مع الاسم ':المُسمى به'. لن يتم حذف الكتب المتضمنة.",
+    'shelves_delete_named' => 'حذف رف الكتب :name',
+    'shelves_delete_explain' => "سيؤدي هذا إلى حذف رف الكتب المسمى ':name'، ولن تحذف الكتب المتضمنة فيه.",
     'shelves_delete_confirmation' => 'هل أنت متأكد من أنك تريد حذف هذا الرف؟',
     'shelves_permissions' => 'أذونات رف الكتب',
     'shelves_permissions_updated' => 'تم تحديث أذونات رف الكتب',
@@ -99,7 +101,7 @@ return [
     'shelves_copy_permissions_to_books' => 'نسخ أذونات الوصول إلى الكتب',
     'shelves_copy_permissions' => 'نسخ الأذونات',
     'shelves_copy_permissions_explain' => 'سيؤدي هذا إلى تطبيق إعدادات الأذونات الحالية لهذا الرف على جميع الكتب المتضمنة فيه. قبل التفعيل، تأكد من حفظ أي تغييرات في أذونات هذا الرف.',
-    'shelves_copy_permission_success' => 'تم نسخ أذونات رف الكتب إلى: عد الكتب',
+    'shelves_copy_permission_success' => 'تم نسخ أذونات رف الكتب إلى :count books',
 
     // Books
     'book' => 'كتاب',
@@ -115,7 +117,7 @@ return [
     'books_create' => 'إنشاء كتاب جديد',
     'books_delete' => 'حذف الكتاب',
     'books_delete_named' => 'حذف كتاب :bookName',
-    'books_delete_explain' => 'سيتم حذف كتاب \':bookName\'. ستتم إزالة جميع الفصول والصفحات.',
+    'books_delete_explain' => 'سيتم حذف كتاب \':bookName\'، وأيضا حذف جميع الفصول والصفحات.',
     'books_delete_confirmation' => 'تأكيد حذف الكتاب؟',
     'books_edit' => 'تعديل الكتاب',
     'books_edit_named' => 'تعديل كتاب :bookName',
@@ -215,7 +217,7 @@ return [
     'pages_revisions_created_by' => 'أنشئ بواسطة',
     'pages_revisions_date' => 'تاريخ المراجعة',
     'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'مراجعة #: رقم تعريفي',
+    'pages_revisions_numbered' => 'مراجعة #:id',
     'pages_revisions_numbered_changes' => 'مراجعة #: رقم تعريفي التغييرات',
     'pages_revisions_changelog' => 'سجل التعديل',
     'pages_revisions_changes' => 'التعديلات',
@@ -228,7 +230,7 @@ return [
     'pages_permissions_active' => 'أذونات الصفحة مفعلة',
     'pages_initial_revision' => 'نشر مبدئي',
     'pages_initial_name' => 'صفحة جديدة',
-    'pages_editing_draft_notification' => 'جار تعديل مسودة لم يتم حفظها من :timeDiff.',
+    'pages_editing_draft_notification' => 'جارٍ تعديل مسودة لم يتم حفظها من :timeDiff.',
     'pages_draft_edited_notification' => 'تم تحديث هذه الصفحة منذ ذلك الوقت. من الأفضل التخلص من هذه المسودة.',
     'pages_draft_edit_active' => [
         'start_a' => ':count من المستخدمين بدأوا بتعديل هذه الصفحة',
@@ -237,7 +239,7 @@ return [
         'time_b' => 'في آخر :minCount دقيقة/دقائق',
         'message' => 'وقت البدء: احرص على عدم الكتابة فوق تحديثات بعضنا البعض!',
     ],
-    'pages_draft_discarded' => 'تم التخلص من المسودة. تم تحديث المحرر بمحتوى الصفحة الحالي',
+    'pages_draft_discarded' => 'تم التخلص من المسودة وتحديث المحرر بمحتوى الصفحة الحالي',
     'pages_specific' => 'صفحة محددة',
     'pages_is_template' => 'قالب الصفحة',
 
@@ -255,14 +257,14 @@ return [
     'tags_remove' => 'إزالة هذه العلامة',
     'attachments' => 'المرفقات',
     'attachments_explain' => 'ارفع بعض الملفات أو أرفق بعض الروابط لعرضها بصفحتك. ستكون الملفات والروابط معروضة في الشريط الجانبي للصفحة.',
-    'attachments_explain_instant_save' => 'سÙ\8aتÙ\85 Ø­Ù\81ظ Ø§Ù\84تغÙ\8aÙ\8aرات Ù\87Ù\86ا Ø¨Ù\84حظتÙ\87ا',
+    'attachments_explain_instant_save' => 'سÙ\8aتÙ\85 Ø­Ù\81ظ Ø§Ù\84تغÙ\8aÙ\8aرات Ù\87Ù\86ا Ø¢Ù\86Ù\8aا.',
     'attachments_items' => 'العناصر المرفقة',
     'attachments_upload' => 'رفع ملف',
     'attachments_link' => 'إرفاق رابط',
     'attachments_set_link' => 'تحديد الرابط',
     'attachments_delete' => 'هل أنت متأكد من أنك تريد حذف هذا المرفق؟',
     'attachments_dropzone' => 'أسقط الملفات أو اضغط هنا لإرفاق ملف',
-    'attachments_no_files' => 'لم يتم رفع أي ملفات',
+    'attachments_no_files' => 'لم تُرفع أي ملفات',
     'attachments_explain_link' => 'بالإمكان إرفاق رابط في حال عدم تفضيل رفع ملف. قد يكون الرابط لصفحة أخرى أو لملف في أحد خدمات التخزين السحابي.',
     'attachments_link_name' => 'اسم الرابط',
     'attachment_link' => 'رابط المرفق',
@@ -287,7 +289,7 @@ return [
     'templates_prepend_content' => 'بادئة محتوى الصفحة',
 
     // Profile View
-    'profile_user_for_x' => 'المستخدم لـ : الوقت',
+    'profile_user_for_x' => 'المستخدم لـ :time',
     'profile_created_content' => 'المحتوى المنشأ',
     'profile_not_created_pages' => 'لم يتم إنشاء أي صفحات بواسطة :userName',
     'profile_not_created_chapters' => 'لم يتم إنشاء أي فصول بواسطة :userName',
@@ -299,7 +301,7 @@ return [
     'comments' => 'تعليقات',
     'comment_add' => 'إضافة تعليق',
     'comment_placeholder' => 'ضع تعليقاً هنا',
-    'comment_count' => '{0} ا توجد تعليقات|{1} تعليق واحد|{2} تعليقان|[3,*] :count تعليقات',
+    'comment_count' => '{0} لا توجد تعليقات|{1} تعليق واحد|{2} تعليقان[3,*] :count تعليقات',
     'comment_save' => 'حفظ التعليق',
     'comment_saving' => 'جار حفظ التعليق...',
     'comment_deleting' => 'جار حذف التعليق...',
@@ -313,8 +315,8 @@ return [
     'comment_in_reply_to' => 'رداً على :commentId',
 
     // Revision
-    'revision_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف هذا الإصدار؟',
-    'revision_restore_confirm' => 'هل أنت متأكد من أنك تريد استعادة هذا الإصدار؟ سيتم استبدال محتوى الصفحة الحالية.',
-    'revision_delete_success' => 'تم حذف الإصدار',
-    'revision_cannot_delete_latest' => 'لايمكن حذف آخر إصدار.'
+    'revision_delete_confirm' => 'هل أنت متأكد من أنك تريد حذف هذه المراجعة؟',
+    'revision_restore_confirm' => 'هل أنت متأكد من أنك تريد استعادة هذه المراجعة؟ سيتم استبدال محتوى الصفحة الحالية.',
+    'revision_delete_success' => 'تم حذف المراجعة',
+    'revision_cannot_delete_latest' => 'لايمكن حذف آخر مراجعة.'
 ];
index 93e8201ab7599e3a11a40fb44d076461d07137b8..829571c585c921ad489a9e8a3f99a3690fd68b98 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'لم يتم العثور على الصفحة',
     'sorry_page_not_found' => 'عفواً, لا يمكن العثور على الصفحة التي تبحث عنها.',
     'sorry_page_not_found_permission_warning' => 'إذا كنت تتوقع أن تكون هذه الصفحة موجودة، قد لا يكون لديك تصريح بمشاهدتها.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'العودة للصفحة الرئيسية',
     'error_occurred' => 'حدث خطأ',
     'app_down' => ':appName لا يعمل حالياً',
index 20789d842eacbe545e17572081a120542aa8127d..88f68a2f8568c0540a7881c3c70592c5b3772044 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index a495733549313f8ece45e61a9b29fd97c6983451..90218a30ac00ed60f511c4b620aa3ddff4cdf329 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'изтрит рафт',
     'bookshelf_delete_notification'    => 'Рафтът беше успешно изтрит',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'коментирано на',
     'permissions_update'          => 'updated permissions',
index d403dc306070da9971c857d11e2d30effabfa066..7c833bd0d21855605bd2da5fe44c013fbdd22986 100644 (file)
@@ -33,13 +33,17 @@ return [
     'copy' => 'Копирай',
     'reply' => 'Отговори',
     'delete' => 'Изтрий',
-    'delete_confirm' => 'Confirm Deletion',
+    'delete_confirm' => 'Потвърдете изтриването',
     'search' => 'Търси',
     'search_clear' => 'Изчисти търсенето',
     'reset' => 'Нулирай',
     'remove' => 'Премахване',
     'add' => 'Добави',
     'fullscreen' => 'Пълен екран',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Опции за сортиране',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Трасиране',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Профил меню',
     'view_profile' => 'Разглеждане на профил',
     'edit_profile' => 'Редактиране на профила',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Информация',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Съдържание',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Ако имате проблеми с бутона ":actionText" по-горе, копирайте и поставете URL адреса по-долу в уеб браузъра си:',
@@ -81,6 +88,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'Лични данни',
+    'terms_of_service' => 'Общи условия',
 ];
index b15aa6f796fb6cad2390933917c6289329695700..4be89bfb5a9ed2e8543e6c764a33c92621080f0a 100644 (file)
@@ -15,7 +15,7 @@ return [
     'image_load_more' => 'Зареди повече',
     'image_image_name' => 'Име на изображението',
     'image_delete_used' => 'Това изображение е използвано в страницата по-долу.',
-    'image_delete_confirm_text' => 'Are you sure you want to delete this image?',
+    'image_delete_confirm_text' => 'Сигурни ли сте, че искате да изтриете това изображение?',
     'image_select_image' => 'Изберете изображение',
     'image_dropzone' => 'Поставете тук изображение или кликнете тук за да качите',
     'images_deleted' => 'Изображението е изтрито',
index 2e35b5afa42fc8a5f617c56ce228ce48235c1230..accf2157a409f7ad1018cf9aae2b2381fbddc018 100644 (file)
@@ -22,11 +22,13 @@ return [
     'meta_created_name' => 'Създадено преди :timeLength от :user',
     'meta_updated' => 'Актуализирано :timeLength',
     'meta_updated_name' => 'Актуализирано преди :timeLength от :user',
-    'meta_owned_name' => 'Owned by :user',
+    'meta_owned_name' => 'Притежавано от :user',
     'entity_select' => 'Избор на обект',
     'images' => 'Изображения',
     'my_recent_drafts' => 'Моите скорошни драфтове',
     'my_recently_viewed' => 'Моите скорошни преглеждания',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'Моите фаворити',
     'no_pages_viewed' => 'Не сте прегледали никакви страници',
     'no_pages_recently_created' => 'Не са били създавани страници скоро',
     'no_pages_recently_updated' => 'Не са били актуализирани страници скоро',
@@ -40,7 +42,7 @@ return [
     'permissions_intro' => 'Веднъж добавени, тези права ще вземат приоритет над всички други установени права.',
     'permissions_enable' => 'Разреши уникални права',
     'permissions_save' => 'Запази права',
-    'permissions_owner' => 'Owner',
+    'permissions_owner' => 'Собственик',
 
     // Search
     'search_results' => 'Резултати от търсенето',
@@ -49,7 +51,7 @@ return [
     'search_no_pages' => 'Няма страници отговарящи на търсенето',
     'search_for_term' => 'Търси :term',
     'search_more' => 'Още резултати',
-    'search_advanced' => 'Advanced Search',
+    'search_advanced' => 'Подробно търсене',
     'search_terms' => 'Search Terms',
     'search_content_type' => 'Тип на съдържание',
     'search_exact_matches' => 'Точни съвпадения',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Задаване на права',
     'search_created_by_me' => 'Създадено от мен',
     'search_updated_by_me' => 'Обновено от мен',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Притежаван от мен',
     'search_date_options' => 'Настройки на дати',
     'search_updated_before' => 'Обновено преди',
     'search_updated_after' => 'Обновено след',
@@ -260,7 +262,7 @@ return [
     'attachments_upload' => 'Прикачен файл',
     'attachments_link' => 'Прикачване на линк',
     'attachments_set_link' => 'Поставяне на линк',
-    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_delete' => 'Сигурни ли сте, че искате да изтриете прикачения файл?',
     'attachments_dropzone' => 'Поставете файлове или цъкнете тук за да прикачите файл',
     'attachments_no_files' => 'Няма прикачени фалове',
     'attachments_explain_link' => 'Може да прикачите линк, ако не искате да качвате файл. Този линк може да бъде към друга страница или към файл в облакова пространство.',
index 4b062af06b557237fe37481dd0e6b004afb0f27b..ea6d497f06903aa7658c3163b95a02edfef51521 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Страницата не е намерена',
     'sorry_page_not_found' => 'Страницата, която търсите не може да бъде намерена.',
     'sorry_page_not_found_permission_warning' => 'Ако смятате, че тази страница съществува, най-вероятно нямате право да я преглеждате.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Назад към Начало',
     'error_occurred' => 'Възникна грешка',
     'app_down' => ':appName не е достъпно в момента',
index 5fb24056e05fd5011f09afe640a019df530cf019..c32d3a5dd9d05f9681f5771a1ac4717d0922f90a 100644 (file)
@@ -35,13 +35,13 @@ return [
     '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_footer_links' => 'Footer Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
+    'app_homepage_desc' => 'Изберете начална страница, която ще замени изгледа по подразбиране. Дефинираните права на страницата, която е избрана ще бъдат игнорирани.',
+    'app_homepage_select' => 'Избери страница',
+    'app_footer_links' => 'Футър линкове',
+    'app_footer_links_desc' => 'Добави линк в съдържанието на футъра. Добавените линкове ще се показват долу в повечето страници, включително и в страниците, в които логването не е задължително. Можете да използвате заместител "trans::<key>", за да използвате дума дефинирана от системата. Пример: Използването на "trans::common.privacy_policy" ще покаже "Лични данни" или на "trans::common.terms_of_service" ще покаже "Общи условия".',
     'app_footer_links_label' => 'Link Label',
-    'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links_url' => 'Линк URL',
+    'app_footer_links_add' => 'Добави футър линк',
     '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.',
@@ -80,20 +80,20 @@ return [
     '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.',
-    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
-    'maint_recycle_bin_open' => 'Open Recycle Bin',
+    'maint_send_test_email_run' => 'Изпрати тестов имейл',
+    'maint_send_test_email_success' => 'Имейл изпратен на :address',
+    'maint_send_test_email_mail_subject' => 'Тестов Имейл',
+    'maint_send_test_email_mail_greeting' => 'Изпращането на Имейл работи!',
+    'maint_send_test_email_mail_text' => 'Поздравления! След като получихте този имейл, Вашите имейл настройки са конфигурирани правилно.',
+    'maint_recycle_bin_desc' => 'Изтрити рафти, книги, глави и страници се преместват в кошчето, откъдето можете да ги възстановите или изтриете завинаги. Стари съдържания в кошчето ще бъдат изтрити автоматично след време, в зависимост от настройките на системата.',
+    'maint_recycle_bin_open' => 'Отвори Кошчето',
 
     // Recycle Bin
-    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin' => 'Кошче',
     'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
-    'recycle_bin_deleted_item' => 'Deleted Item',
-    'recycle_bin_deleted_by' => 'Deleted By',
-    'recycle_bin_deleted_at' => 'Deletion Time',
+    'recycle_bin_deleted_item' => 'Изтрит предмет',
+    'recycle_bin_deleted_by' => 'Изтрит от',
+    'recycle_bin_deleted_at' => 'Час на изтриване',
     'recycle_bin_permanently_delete' => 'Permanently Delete',
     'recycle_bin_restore' => 'Restore',
     'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
@@ -111,27 +111,27 @@ return [
     '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_event_filter_no_filter' => 'Без филтър',
+    'audit_deleted_item' => 'Изтрит предмет',
+    'audit_deleted_item_name' => 'Име: :name',
+    'audit_table_user' => 'Потребител',
+    'audit_table_event' => 'Събитие',
     'audit_table_related' => 'Related Item or Detail',
-    'audit_table_date' => 'Activity Date',
-    'audit_date_from' => 'Date Range From',
-    'audit_date_to' => 'Date Range To',
+    'audit_table_date' => 'Дата на активност',
+    'audit_date_from' => 'Време от',
+    'audit_date_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',
+    'roles' => 'Роли',
+    'role_user_roles' => 'Потребителски роли',
+    'role_create' => 'Създай нова роля',
+    'role_create_success' => 'Ролята беше успешно създадена',
+    'role_delete' => 'Изтрий роля',
+    'role_delete_confirm' => 'Това ще изтрие ролята \':roleName\'.',
+    'role_delete_users_assigned' => 'В тази роля се намират :userCount потребители. Ако искате да преместите тези потребители в друга роля, моля изберете нова роля отдолу.',
+    'role_delete_no_migration' => "Не премествай потребителите в нова роля",
+    'role_delete_sure' => 'Сигурни ли сте, че искате да изтриете тази роля?',
+    'role_delete_success' => 'Ролята беше успешно изтрита',
     'role_edit' => 'Редактиране на роля',
     'role_details' => 'Детайли на роля',
     'role_name' => 'Име на ролята',
@@ -146,24 +146,24 @@ 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' => 'Тези настойки за достъп контролират достъпа по подразбиране до активите в системата. Настойките за достъп до книги, глави и страници ще отменят тези настройки.',
+    'roles_system_warning' => 'Важно: Добавянето на потребител в някое от горните три роли може да му позволи да промени собствените си права или правата на другите в системата. Възлагайте тези роли само на доверени потребители.',
+    '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',
+    'role_save' => 'Запази ролята',
+    'role_update_success' => 'Ролята беше успешно актуализирана',
+    'role_users' => 'Потребители в тази роля',
+    'role_users_none' => 'В момента няма потребители, назначени за тази роля',
 
     // Users
-    'users' => 'Users',
-    'user_profile' => 'User Profile',
-    'users_add_new' => 'Add New User',
-    'users_search' => 'Search Users',
-    'users_latest_activity' => 'Latest Activity',
-    'users_details' => 'User Details',
+    'users' => 'Потребители',
+    'user_profile' => 'Потребителски профил',
+    'users_add_new' => 'Добави нов потребител',
+    'users_search' => 'Търси Потребители',
+    'users_latest_activity' => 'Последна активност',
+    'users_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',
@@ -176,13 +176,13 @@ return [
     '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_migrate_ownership' => 'Migrate Ownership',
+    'users_delete' => 'Изтрий потребител',
+    'users_delete_named' => 'Изтрий потребителя :userName',
+    'users_delete_warning' => 'Това изцяло ще изтрие този потребител с името \':userName\' от системата.',
+    'users_delete_confirm' => 'Сигурни ли сте, че искате да изтриете този потребител?',
+    'users_migrate_ownership' => 'Мигрирайте собствеността на сайта',
     'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
-    'users_none_selected' => 'No user selected',
+    'users_none_selected' => 'Няма избрани потребители',
     'users_delete_success' => 'User successfully removed',
     'users_edit' => 'Edit User',
     'users_edit_profile' => 'Edit Profile',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 4031de2ae743b75bafd49195ff06b29a347cbf22..3f22751dd567a0cb1a35df2b20020e824fcbf426 100644 (file)
@@ -8,67 +8,67 @@
 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.',
+    'accepted'             => ':attribute трябва да бъде одобрен.',
+    'active_url'           => ':attribute не е валиден URL адрес.',
+    'after'                => ':attribute трябва да е дата след :date.',
+    'alpha'                => ':attribute може да съдържа само букви.',
+    'alpha_dash'           => ':attribute може да съдържа само букви, числа, тире и долна черта.',
+    'alpha_num'            => ':attribute може да съдържа само букви и числа.',
+    'array'                => ':attribute трябва да е масив (array).',
+    'before'               => ':attribute трябва да е дата след :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.',
+        'numeric' => ':attribute трябва да е между :min и :max.',
+        'file'    => ':attribute трябва да е между :min и :max килобайта.',
+        'string'  => 'Дължината на :attribute трябва да бъде между :min и :max символа.',
         '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.',
+    'boolean'              => 'Полето :attribute трябва да съдържа булева стойност (true или false).',
+    'confirmed'            => 'Потвърждението на :attribute не съвпада.',
+    'date'                 => ':attribute не е валидна дата.',
+    'date_format'          => ':attribute не е в посоченият формат - :format.',
+    'different'            => ':attribute и :other трябва да са различни.',
+    'digits'               => ':attribute трябва да съдържа :digits цифри.',
+    'digits_between'       => ':attribute трябва да бъде с дължина между :min и :max цифри.',
+    'email'                => ':attribute трябва да бъде валиден имейл адрес.',
+    'ends_with' => ':attribute трябва да свършва с един от следните символи: :values',
+    'filled'               => 'Полето :attribute е задължителен.',
     '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.',
+        'numeric' => ':attribute трябва да бъде по-голям от :value.',
+        'file'    => 'Големината на :attribute трябва да бъде по-голямо от :value килобайта.',
+        'string'  => 'Дължината на :attribute трябва да бъде по-голямо от :value символа.',
         '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.',
+        'file'    => 'Големината на :attribute трябва да бъде по-голямо или равно на :value килобайта.',
+        'string'  => 'Дължината на :attribute трябва да бъде по-голямо или равно на :value символа.',
         '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.',
+    'exists'               => 'Избраният :attribute е невалиден.',
+    'image'                => ':attribute трябва да e изображение.',
+    'image_extension'      => ':attribute трябва да е валиден и/или допустим графичен файлов формат.',
+    'in'                   => 'Избраният :attribute е невалиден.',
+    'integer'              => ':attribute трябва да бъде цяло число.',
+    'ip'                   => ':attribute трябва да бъде валиден IP адрес.',
+    'ipv4'                 => ':attribute трябва да бъде валиден IPv4 адрес.',
+    'ipv6'                 => ':attribute трябва да бъде валиден IPv6 адрес.',
+    'json'                 => ':attribute трябва да съдържа валиден JSON.',
     '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.',
+        'numeric' => ':attribute трябва да бъде по-малко от :value.',
+        'file'    => 'Големината на :attribute трябва да бъде по-малко от :value килобайта.',
+        'string'  => 'Дължината на :attribute трябва да бъде по-малко от :value символа.',
         '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.',
+        'numeric' => ':attribute трябва да бъде по-малко или равно на :value.',
+        'file'    => 'Големината на :attribute трябва да бъде по-малко или равно на :value килобайта.',
+        'string'  => 'Дължината на :attribute трябва да бъде по-малко или равно на :value символа.',
         '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.',
+        'numeric' => ':attribute не трябва да бъде по-голям от :max.',
+        'file'    => 'Големината на :attribute не може да бъде по-голямо от :value килобайта.',
+        'string'  => 'Дължината на :attribute не може да бъде по-голямо от :value символа.',
         'array'   => 'The :attribute may not have more than :max items.',
     ],
     'mimes'                => 'The :attribute must be a file of type: :values.',
index a9c7bfb52e55005821c991eefaf2c7e521cd1ef5..c4bb5a27e75f2fc1926fff1cb0ace0144af6e5bd 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'je izbrisao/la policu za knjige',
     'bookshelf_delete_notification'    => 'Polica za knjige Uspješno Izbrisana',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'je komentarisao/la na',
     'permissions_update'          => 'je ažurirao/la dozvole',
index 44d04b3e37b22334211c923b440c721335fa75b0..4a1af9de083992c090aec20cb9b1abba763754ca 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Ukloni',
     'add' => 'Dodaj',
     'fullscreen' => 'Prikaz preko čitavog ekrana',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Opcije sortiranja',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Navigacijske stavke',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Meni profila',
     'view_profile' => 'Pogledaj profil',
     'edit_profile' => 'Izmjeni profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informacije',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Sadržaj',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Ukoliko imate poteškoća sa pritiskom na ":actionText" dugme, kopirajte i zaljepite URL koji se nalazi ispod u vaš web pretraživač:',
index 1e5350fe509af7922b5c616a9ed911a7cd91e8f9..14f4e351cfb8e82fdde2ae89aaf9adf96444e526 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Slike',
     'my_recent_drafts' => 'Moje nedavne skice',
     'my_recently_viewed' => 'Moji nedavni pregledi',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Niste pogledali nijednu stranicu',
     'no_pages_recently_created' => 'Nijedna stranica nije napravljena nedavno',
     'no_pages_recently_updated' => 'Niijedna stranica nije ažurirana nedavno',
index aa16c11771fa215c8ae28ba4f1e94ab2b6dc0cb1..f3696061954e3491a06033937d12bede410afbc3 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Stranica nije pronađena',
     'sorry_page_not_found' => 'Stranica koju ste tražili nije pronađena.',
     'sorry_page_not_found_permission_warning' => 'Ako ste očekivali da ova stranica postoji, možda nemate privilegije da joj pristupite.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Nazad na početnu stranu',
     'error_occurred' => 'Desila se greška',
     'app_down' => ':appName trenutno nije u funkciji',
index ab7f153224afd41bb90e9cd54a9472895727b619..8a7946b1281269a0f62d17d6dacea2afd466f56b 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 38e4d4eb550646755aeebfbce7372735c2c320e6..604eaeda4f172af52fe049d3568e57c017456050 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'ha suprimit un prestatge',
     'bookshelf_delete_notification'    => 'Prestatge suprimit correctament',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'ha comentat a',
     'permissions_update'          => 'ha actualitzat els permisos',
index e6d15e898516ebccc965044ce95483fc5bcb3061..a66f75f94ee8e96d19816457fcef07e8219d0f00 100644 (file)
@@ -6,8 +6,8 @@
  */
 return [
 
-    'failed' => 'Les credencials no coincideixen amb les que tenim emmagatzemades.',
-    'throttle' => 'Massa intents d\'iniciar la sessió. Torneu-ho a provar d\'aquí a :seconds segons.',
+    'failed' => 'Les credencials no coincideixen amb les que hi ha emmagatzemades.',
+    'throttle' => 'Massa intents d\'inici de sessió. Torna-ho a provar d\'aquí a :seconds segons.',
 
     // Login & Register
     'sign_up' => 'Registra-m\'hi',
@@ -28,7 +28,7 @@ return [
     'create_account' => 'Crea el compte',
     'already_have_account' => 'Ja teniu un compte?',
     'dont_have_account' => 'No teniu cap compte?',
-    'social_login' => 'Inici de sessió social',
+    'social_login' => 'Inici de sessió amb xarxes social',
     'social_registration' => 'Registre social',
     'social_registration_text' => 'Registreu-vos i inicieu la sessió fent servir un altre servei.',
 
index d3781c11afd60b4b248c8a23e14f481d4fa4dbef..03d38a2d3d2d0a548ab4cff1134e5dfe2a15bbd1 100644 (file)
@@ -7,7 +7,7 @@ return [
     // Buttons
     'cancel' => 'Cancel·la',
     'confirm' => 'D\'acord',
-    'back' => 'Endarrere',
+    'back' => 'Enrere',
     'save' => 'Desa',
     'continue' => 'Continua',
     'select' => 'Selecciona',
@@ -40,6 +40,10 @@ return [
     'remove' => 'Elimina',
     'add' => 'Afegeix',
     'fullscreen' => 'Pantalla completa',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Opcions d\'ordenació',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Ordre ascendent',
     'sort_descending' => 'Ordre descendent',
     'sort_name' => 'Nom',
-    'sort_default' => 'Default',
+    'sort_default' => 'Per defecte',
     'sort_created_at' => 'Data de creació',
     'sort_updated_at' => 'Data d\'actualització',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Ruta de navegació',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Menú del perfil',
     'view_profile' => 'Mostra el perfil',
     'edit_profile' => 'Edita el perfil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informació',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Contingut',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Si teniu problemes per fer clic al botó ":actionText", copieu i enganxeu l\'URL següent al vostre navegador web:',
index 1426168f4b898b7e68dfdb1d283f7c4e853d8171..55921f3b619954debf061ff6c01d206b2d2fcfac 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Imatges',
     'my_recent_drafts' => 'Els vostres esborranys recents',
     'my_recently_viewed' => 'Les vostres visualitzacions recents',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'No heu vist cap pàgina',
     'no_pages_recently_created' => 'No s\'ha creat cap pàgina fa poc',
     'no_pages_recently_updated' => 'No s\'ha actualitzat cap pàgina fa poc',
index 22b8d542c1ffc8df94dfa208f00438adf2a41e88..1a413ba885c79cc6cb9084310d68f2fce1232433 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'No s\'ha trobat la pàgina',
     'sorry_page_not_found' => 'No hem pogut trobar la pàgina que cerqueu.',
     'sorry_page_not_found_permission_warning' => 'Si esperàveu que existís, és possible que no tingueu permisos per a veure-la.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Torna a l\'inici',
     'error_occurred' => 'S\'ha produït un error',
     'app_down' => ':appName està fora de servei en aquests moments',
index 1fb771fe9350917e021b51915c7e3e61f3c1314d..1f0f75915e720c04df0d3a62badbff589a6a5d8c 100644 (file)
@@ -7,9 +7,9 @@
 return [
 
     'password' => 'Les contrasenyes han de tenir com a mínim vuit caràcters i la confirmació ha de coincidir.',
-    'user' => "No s'ha trobat cap usuari amb aquesta adreça electrònica.",
-    'token' => 'El testimoni de reinicialització de la contrasenya no és vàlid per a aquesta adreça electrònica.',
-    'sent' => 'Us hem enviat un enllaç per a restablir la contrasenya!',
-    'reset' => 'S\'ha restablert la vostra contrasenya!',
+    'user' => "No s'ha trobat cap usuari amb aquest correu electrònic.",
+    'token' => 'El token de restabliment de contrasenya no és vàlid per aquest correu electrònic.',
+    'sent' => 'T\'hem enviat un enllaç per a restablir la contrasenya!',
+    'reset' => 'S\'ha restablert la teva contrasenya!',
 
 ];
index 6ce3b19035a76e3c06cbb3bbd9e425e3f4e86064..165c014b4df250601d756255a89f5d931354999d 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index bf63a1baa4c422b978dd5f5dee0742f46917a760..c134c36c6621ad28ec6bf3c51b00715c84cd0a58 100644 (file)
@@ -8,8 +8,8 @@
 return [
 
     // Standard laravel validation lines
-    'accepted'             => 'Cal que accepteu el camp :attribute.',
-    'active_url'           => 'El camp :attribute no és un URL vàlid.',
+    'accepted'             => 'Cal que acceptis :attribute.',
+    'active_url'           => 'L\':attribute no és un URL vàlid.',
     'after'                => 'El camp :attribute ha de ser una data posterior a :date.',
     'alpha'                => 'El camp :attribute només pot contenir lletres.',
     'alpha_dash'           => 'El camp :attribute només pot contenir lletres, números, guions i guions baixos.',
index 818369331b6ebea37e9274da3370b717da3ca6d4..e444399cc5a849d89f1c1654342cbb0b9e22bdf1 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'odstranil/a knihovnu',
     'bookshelf_delete_notification'    => 'Knihovna byla úspěšně odstraněna',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'okomentoval/a',
     'permissions_update'          => 'updated permissions',
index edfc44d5e8f3eb20bb4d327a1932c3139fcb4677..b76fa32e4aeb9ff9fbf6f9f03305192a968cdfc6 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Odebrat',
     'add' => 'Přidat',
     'fullscreen' => 'Celá obrazovka',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Možnosti řazení',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Drobečková navigace',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Nabídka profilu',
     'view_profile' => 'Zobrazit profil',
     'edit_profile' => 'Upravit profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informace',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Obsah',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     '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:',
index 210aca563d5ccc6eb3a577401408bca5f7cff873..1a843c93dc3935cf8e466ae02f22c5d1adb10513 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Obrázky',
     'my_recent_drafts' => 'Mé nedávné koncepty',
     'my_recently_viewed' => 'Mé nedávno zobrazené',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     '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',
index 43ba536f0496236363baa7f18e1ff4cf17b398e3..102a1f88b4f09301124a254bcad0d3efb0fcd9dd 100644 (file)
@@ -83,6 +83,9 @@ return [
     '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' => 'Pokud očekáváte, že by stránka měla existovat, možná jen nemáte oprávnění pro její zobrazení.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Návrat domů',
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálně vypnutá',
index 1dc4000aae3e94f7402ee856db318543c932fa80..86b94956f2290d95c4cb2879650eb3a60fe83619 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 264deec609721fd7aafcabf40a3b1b582d10d81b..614d1a8ac5d38b60aafea812815e7271e2259097 100644 (file)
@@ -20,7 +20,7 @@ return [
     'chapter_create'              => 'oprettede kapitel',
     'chapter_create_notification' => 'Kapitel blev oprettet',
     'chapter_update'              => 'opdaterede kapitel',
-    'chapter_update_notification' => 'Kapitel blev opdateret',
+    'chapter_update_notification' => 'Kapitlet blev opdateret',
     'chapter_delete'              => 'slettede kapitel',
     'chapter_delete_notification' => 'Kapitel blev slettet',
     'chapter_move'                => 'flyttede kapitel',
@@ -43,7 +43,11 @@ return [
     'bookshelf_delete'                 => 'slettede bogreol',
     'bookshelf_delete_notification'    => 'Bogreolen blev opdateret',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'kommenterede til',
-    'permissions_update'          => 'updated permissions',
+    'permissions_update'          => 'Tilladelser opdateret',
 ];
index f060b24b8f46d9c4e14d4a6d8a1955feae37cab1..8ea58517493187a77f01582a22590e2ec4aa2957 100644 (file)
@@ -6,8 +6,8 @@
  */
 return [
 
-    'failed' => 'Det indtastede stemmer ikke overens med vores registrering.',
-    'throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds seconds.',
+    'failed' => 'Dee indtastede brugeroplysninger stemmer ikke overens med vores registreringer.',
+    'throttle' => 'For mange mislykkede loginforsøg. Prøv igen om :seconds sekunder.',
 
     // Login & Register
     'sign_up' => 'Registrér',
@@ -21,11 +21,11 @@ return [
     'email' => 'E-mail',
     'password' => 'Adgangskode',
     'password_confirm' => 'Bekræft adgangskode',
-    'password_hint' => 'Skal være på mindst 8 karakterer',
+    'password_hint' => 'Skal være på mindst 7 karakterer',
     'forgot_password' => 'Glemt Adgangskode?',
-    'remember_me' => 'Husk Mig',
+    'remember_me' => 'Husk mig',
     'ldap_email_hint' => 'Angiv venligst din kontos e-mail.',
-    'create_account' => 'Opret Konto',
+    'create_account' => 'Opret konto',
     'already_have_account' => 'Har du allerede en konto?',
     'dont_have_account' => 'Har du ikke en konto?',
     'social_login' => 'Social Log ind',
index 6f7a8f88a078128e31238a255b41ba08deb71b44..0504f5a105ab00625722584e39dbd290eac2c6b4 100644 (file)
@@ -33,13 +33,17 @@ return [
     'copy' => 'Kopier',
     'reply' => 'Besvar',
     'delete' => 'Slet',
-    'delete_confirm' => 'Confirm Deletion',
+    'delete_confirm' => 'Bekræft sletning',
     'search' => 'Søg',
     'search_clear' => 'Ryd søgning',
     'reset' => 'Nulstil',
     'remove' => 'Fjern',
     'add' => 'Tilføj',
     'fullscreen' => 'Fuld skærm',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sorteringsindstillinger',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Sorter stigende',
     'sort_descending' => 'Sorter faldende',
     'sort_name' => 'Navn',
-    'sort_default' => 'Default',
+    'sort_default' => 'Standard',
     'sort_created_at' => 'Oprettelsesdato',
     'sort_updated_at' => 'Opdateringsdato',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Brødkrumme',
 
     // Header
+    'header_menu_expand' => 'Udvid header menu',
     'profile_menu' => 'Profilmenu',
     'view_profile' => 'Vis profil',
     'edit_profile' => 'Redigér Profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Faneblad: Vis sekundær information',
     'tab_content' => 'Indhold',
+    'tab_content_label' => 'Faneblad: Vis primær indhold',
 
     // Email Content
     'email_action_help' => 'Hvis du har problemer med at trykke på ":actionText" knappen, prøv at kopiere og indsætte linket herunder ind i din webbrowser:',
@@ -81,6 +88,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'Privatlivspolitik',
+    'terms_of_service' => 'Tjenestevilkår',
 ];
index 28e1404cd02965dbf40d66873b629ce98e7f711f..9ba511fc5124a2cc5a6bf7c05395796c6bc4c77c 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_text' => 'Are you sure you want to delete this image?',
+    'image_delete_confirm_text' => 'Er du sikker på at du vil slette dette billede?',
     'image_select_image' => 'Vælg billede',
     'image_dropzone' => 'Træk-og-slip billede eller klik her for at uploade',
     'images_deleted' => 'Billede slettet',
@@ -29,6 +29,6 @@ return [
     'code_editor' => 'Rediger kode',
     'code_language' => 'Kodesprog',
     'code_content' => 'Kodeindhold',
-    'code_session_history' => 'Session History',
+    'code_session_history' => 'Sessionshistorik',
     'code_save' => 'Gem kode',
 ];
index 2f54334dae0aba9476a42f773c0622eb0cba647f..36e290d86d8b38ac16dcf6dd8e2fdcd06eba5560 100644 (file)
@@ -22,11 +22,13 @@ return [
     'meta_created_name' => 'Oprettet :timeLength af :user',
     'meta_updated' => 'Opdateret :timeLength',
     'meta_updated_name' => 'Opdateret :timeLength af :user',
-    'meta_owned_name' => 'Owned by :user',
+    'meta_owned_name' => 'Ejet af :user',
     'entity_select' => 'Vælg emne',
     'images' => 'Billeder',
     'my_recent_drafts' => 'Mine seneste kladder',
     'my_recently_viewed' => 'Mine senest viste',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Du har ikke besøgt nogle sider',
     'no_pages_recently_created' => 'Ingen sider er blevet oprettet for nyligt',
     'no_pages_recently_updated' => 'Ingen sider er blevet opdateret for nyligt',
@@ -40,7 +42,7 @@ return [
     'permissions_intro' => 'Når de er aktiveret, vil disse tilladelser have prioritet over alle indstillede rolletilladelser.',
     'permissions_enable' => 'Aktivér tilpassede tilladelser',
     'permissions_save' => 'Gem tilladelser',
-    'permissions_owner' => 'Owner',
+    'permissions_owner' => 'Ejer',
 
     // Search
     'search_results' => 'Søgeresultater',
@@ -49,8 +51,8 @@ return [
     'search_no_pages' => 'Ingen sider matchede søgning',
     'search_for_term' => 'Søgning for :term',
     'search_more' => 'Flere resultater',
-    'search_advanced' => 'Advanced Search',
-    'search_terms' => 'Search Terms',
+    'search_advanced' => 'Avanceret søgning',
+    'search_terms' => 'Søgeord',
     'search_content_type' => 'Indholdstype',
     'search_exact_matches' => 'Nøjagtige matches',
     'search_tags' => 'Tagsøgninger',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Rettigheders sæt',
     'search_created_by_me' => 'Oprettet af mig',
     'search_updated_by_me' => 'Opdateret af mig',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Ejet af mig',
     'search_date_options' => 'Datoindstillinger',
     'search_updated_before' => 'Opdateret før',
     'search_updated_after' => 'Opdateret efter',
@@ -149,7 +151,7 @@ return [
     'chapters_create' => 'Opret nyt kapitel',
     'chapters_delete' => 'Slet kapitel',
     'chapters_delete_named' => 'Slet kapitel :chapterName',
-    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_explain' => 'Dette vil slette kapitlet med navnet \':chapterName\'. Alle sider i dette kapitel vil også blive slettet.',
     'chapters_delete_confirm' => 'Er du sikker på du vil slette dette kapitel?',
     'chapters_edit' => 'Rediger kapitel',
     'chapters_edit_named' => 'Rediger kapitel :chapterName',
@@ -211,7 +213,7 @@ return [
     'pages_revisions' => 'Sidserevisioner',
     'pages_revisions_named' => 'Siderevisioner for :pageName',
     'pages_revision_named' => 'Siderevision for :pageName',
-    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revision_restored_from' => 'Genoprettet fra #:id; :summary',
     'pages_revisions_created_by' => 'Oprettet af',
     'pages_revisions_date' => 'Revisionsdato',
     'pages_revisions_number' => '#',
@@ -260,7 +262,7 @@ return [
     'attachments_upload' => 'Upload fil',
     'attachments_link' => 'Vedhæft link',
     'attachments_set_link' => 'Sæt link',
-    'attachments_delete' => 'Are you sure you want to delete this attachment?',
+    'attachments_delete' => 'Er du sikker på at du vil slette denne vedhæftning?',
     '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.',
@@ -269,7 +271,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_insert_link' => 'Tilføj vedhæftningslink til side',
     '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 0f6287fbc1861a1546fa9ada4690b0bb3a2a43db..806ac8177c2f1a3071bff928db2c1f26497d7884 100644 (file)
@@ -83,6 +83,9 @@ return [
     '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' => 'Hvis du forventede, at denne side skulle eksistere, har du muligvis ikke tilladelse til at se den.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Gå tilbage til hjem',
     'error_occurred' => 'Der opstod en fejl',
     'app_down' => ':appName er nede lige nu',
index e680f3afc56659c692b7fe794fab8cbd602a65a1..7bbc31edbc209d2712b9aa906536425a79254d25 100644 (file)
@@ -9,13 +9,13 @@ return [
     // Common Messages
     'settings' => 'Indstillinger',
     'settings_save' => 'Gem indstillinger',
-    'settings_save_success' => 'Indstillinger gemt',
+    'settings_save_success' => 'Indstillingerne blev gemt',
 
     // App Settings
     'app_customization' => 'Tilpasning',
-    'app_features_security' => 'Funktioner & sikkerhed',
-    'app_name' => 'Programnavn',
-    'app_name_desc' => 'Dette er navnet vist i headeren og i systemafsendte E-Mails.',
+    'app_features_security' => 'Funktionalitet og sikkerhed',
+    'app_name' => 'Applikationsnavn',
+    'app_name_desc' => 'Dette navn vises i headeren og i alle e-mails sendt fra systemet.',
     'app_name_header' => 'Vis navn i header',
     'app_public_access' => 'Offentlig adgang',
     'app_public_access_desc' => 'Aktivering af denne funktion giver besøgende, der ikke er logget ind, adgang til indhold i din BookStack-instans.',
@@ -24,24 +24,24 @@ return [
     'app_public_viewing' => 'Tillad offentlig visning?',
     'app_secure_images' => 'Højere sikkerhed for billeduploads',
     'app_secure_images_toggle' => 'Aktiver højere sikkerhed for billeduploads',
-    'app_secure_images_desc' => 'Af ydeevneårsager er alle billeder offentlige. Denne funktion tilføjer en tilfældig, vanskelig at gætte streng foran billed-Url\'er. Sørg for, at mappeindekser ikke er aktiveret for at forhindre nem adgang.',
+    'app_secure_images_desc' => 'Af performanceårsager er alle billeder offentlige. Denne funktion tilføjer en tilfældig, vanskelig at gætte streng foran billed-url\'er. Sørg for, at mappeindeksering ikke er aktiveret for at forhindre nem adgang.',
     'app_editor' => 'Sideeditor',
     'app_editor_desc' => 'Vælg hvilken editor der skal bruges af alle brugere til at redigere sider.',
-    'app_custom_html' => 'Tilpasset HTML head-indhold',
-    'app_custom_html_desc' => 'Al indhold tilføjet her, vil blive indsat i bunden af <head> sektionen på alle sider. Dette er brugbart til overskrivning af styling og tilføjelse af analysekode.',
-    'app_custom_html_disabled_notice' => 'Brugerdefineret HTML-head indhold er deaktiveret på denne indstillingsside for at sikre, at ødelæggende ændringer kan rettes.',
-    'app_logo' => 'Programlogo',
-    'app_logo_desc' => 'Dette billede skal være 43px højt. <br> Større billeder vil blive skaleret ned.',
-    'app_primary_color' => 'Primær programfarve',
+    'app_custom_html' => 'Tilpasset HTML head indhold',
+    'app_custom_html_desc' => 'Alt indhold tilføjet her, vil blive indsat i bunden af <head> sektionen på alle sider. Dette er brugbart til overskrivning af styles og tilføjelse af analytics kode.',
+    'app_custom_html_disabled_notice' => 'Brugerdefineret HTML head indhold er deaktiveret på denne indstillingsside for at, at ændringer kan rulles tilbage.',
+    'app_logo' => 'Applikationslogo',
+    'app_logo_desc' => 'Dette billede skal være 43px højt. <br>Store billeder vil blive skaleret ned.',
+    'app_primary_color' => 'Primær applikationsfarve',
     'app_primary_color_desc' => 'Sætter den primære farve for applikationen herunder banneret, knapper og links.',
-    'app_homepage' => 'Programforside',
-    'app_homepage_desc' => 'Vælg en visning, der skal vises på startsiden i stedet for standardvisningen. Sidetilladelser ignoreres for valgte sider.',
+    'app_homepage' => 'Applikationsforside',
+    'app_homepage_desc' => 'Vælg en visning, der skal vises på forsiden i stedet for standardvisningen. Sidetilladelser ignoreres for de valgte sider.',
     'app_homepage_select' => 'Vælg en side',
-    'app_footer_links' => 'Footer Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
+    'app_footer_links' => 'Footer links',
+    'app_footer_links_desc' => 'Tilføj links til footeren. Linksene vil blive vist nederst på de fleste sider, inkluderet sider, som ikke kræver login. Brug en label med "trans::<key>" for at bruge systemdefinerede oversættelser. For eksempel: "trans::common.privacy_policy" giver den oversatte tekst "Privacy Policy" og "trans::common.terms_of_service" vil give den oversatte tekst "Terms of Service".',
+    'app_footer_links_label' => 'Link label',
     'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links_add' => 'Tilføj footer link',
     'app_disable_comments' => 'Deaktiver kommentarer',
     'app_disable_comments_toggle' => 'Deaktiver kommentar',
     'app_disable_comments_desc' => 'Deaktiverer kommentarer på tværs af alle sider i applikationen. <br> Eksisterende kommentarer vises ikke.',
@@ -73,7 +73,7 @@ return [
     'maint' => 'Vedligeholdelse',
     'maint_image_cleanup' => 'Ryd op i billeder',
     'maint_image_cleanup_desc' => "Scanner side & revisionsindhold for at kontrollere, hvilke billeder og tegninger, der i øjeblikket er i brug, og hvilke billeder, der er overflødige. Sørg for, at du opretter en komplet database og billedbackup, før du kører dette.",
-    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_delete_images_only_in_revisions' => 'Slet også billeder, der kun findes i gamle siderevisioner',
     'maint_image_cleanup_run' => 'Kør Oprydning',
     'maint_image_cleanup_warning' => 'der blev fundet :count potentielt ubrugte billeder. Er du sikker på, at du vil slette disse billeder?',
     'maint_image_cleanup_success' => ':count: potentielt ubrugte billeder fundet og slettet!',
@@ -85,41 +85,41 @@ return [
     'maint_send_test_email_mail_subject' => 'Test E-Mail',
     '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.',
-    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
-    'maint_recycle_bin_open' => 'Open Recycle Bin',
+    'maint_recycle_bin_desc' => 'Slettede hylder, bøger, kapitler og sider overføres til papirkurven, så de kan gendannes eller slettes permanent. Ældre elementer i papirkurven fjernes automatisk efter et stykke tid afhængigt af systemets konfiguration.',
+    'maint_recycle_bin_open' => 'Åbn papirkurven',
 
     // Recycle Bin
-    'recycle_bin' => 'Recycle Bin',
-    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
-    'recycle_bin_deleted_item' => 'Deleted Item',
-    'recycle_bin_deleted_by' => 'Deleted By',
-    'recycle_bin_deleted_at' => 'Deletion Time',
-    'recycle_bin_permanently_delete' => 'Permanently Delete',
-    'recycle_bin_restore' => 'Restore',
-    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
-    'recycle_bin_empty' => 'Empty Recycle Bin',
-    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
-    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
-    'recycle_bin_destroy_list' => 'Items to be Destroyed',
-    'recycle_bin_restore_list' => 'Items to be Restored',
-    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
-    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
-    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
-    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+    'recycle_bin' => 'Papirkurv',
+    'recycle_bin_desc' => 'Her kan du gendanne elementer, der er blevet slettet eller vælge at permanent fjerne dem fra systemet. Denne liste er ufiltreret, i modsætning til lignende aktivitetslister i systemet, hvor tilladelsesfiltre anvendes.',
+    'recycle_bin_deleted_item' => 'Slettet element',
+    'recycle_bin_deleted_by' => 'Slettet af',
+    'recycle_bin_deleted_at' => 'Sletningstidspunkt',
+    'recycle_bin_permanently_delete' => 'Slet permanent',
+    'recycle_bin_restore' => 'Gendan',
+    'recycle_bin_contents_empty' => 'Papirkurven er tom',
+    'recycle_bin_empty' => 'Tøm papirkurv',
+    'recycle_bin_empty_confirm' => 'Dette vil permanent slette alle elementer i papirkurven, inkluderet hvert elements indhold. Er du sikker på, at du vil tømme papirkurven?',
+    'recycle_bin_destroy_confirm' => 'Denne handling sletter dette element permanent, sammen med elementerne anført nedenfor, fra systemet. Du vil ikke være i stand til at gendanne dette indhold. Er du sikker på, at du vil slette dette element permanent?',
+    'recycle_bin_destroy_list' => 'Elementer der skal slettes',
+    'recycle_bin_restore_list' => 'Elementer der skal gendannes',
+    'recycle_bin_restore_confirm' => 'Denne handling vil gendanne det slettede element, herunder alle underelementer, til deres oprindelige placering. Hvis den oprindelige placering siden er blevet slettet, og nu er i papirkurven, vil det overordnede element også skulle gendannes.',
+    'recycle_bin_restore_deleted_parent' => 'Det overordnede element til dette element er også blevet slettet. Disse vil forblive slettet indtil det overordnede også er gendannet.',
+    'recycle_bin_destroy_notification' => 'Slettede :count elementer fra papirkurven.',
+    'recycle_bin_restore_notification' => 'Gendannede :count elementer fra papirkurven.',
 
     // 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_related' => 'Related Item or Detail',
-    'audit_table_date' => 'Activity Date',
-    'audit_date_from' => 'Date Range From',
-    'audit_date_to' => 'Date Range To',
+    'audit' => 'Revisionslog',
+    'audit_desc' => 'Denne revisionslog viser en liste over aktiviteter sporet i systemet. Denne liste er ufiltreret i modsætning til lignende aktivitetslister i systemet, hvor tilladelsesfiltre anvendes.',
+    'audit_event_filter' => 'Event filter',
+    'audit_event_filter_no_filter' => 'Intet filter',
+    'audit_deleted_item' => 'Element slettet',
+    'audit_deleted_item_name' => 'Navn: :name',
+    'audit_table_user' => 'Bruger',
+    'audit_table_event' => 'Hændelse',
+    'audit_table_related' => 'Relateret element eller detalje',
+    'audit_table_date' => 'Aktivitetsdato',
+    'audit_date_from' => 'Datointerval fra',
+    'audit_date_to' => 'Datointerval til',
 
     // Role Settings
     'roles' => 'Roller',
@@ -146,7 +146,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.',
+    'roles_system_warning' => 'Vær opmærksom på, at adgang til alle af de ovennævnte tre tilladelser, kan give en bruger mulighed for at ændre deres egne brugerrettigheder eller brugerrettigheder for andre i systemet. Tildel kun roller med disse tilladelser til betroede brugere.',
     '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',
@@ -162,7 +162,7 @@ return [
     'user_profile' => 'Brugerprofil',
     'users_add_new' => 'Tilføj ny bruger',
     'users_search' => 'Søg efter brugere',
-    'users_latest_activity' => 'Latest Activity',
+    'users_latest_activity' => 'Seneste aktivitet',
     'users_details' => 'Brugeroplysninger',
     'users_details_desc' => 'Angiv et visningsnavn og en E-Mail-adresse for denne bruger. E-Mail-adressen bruges til at logge ind på applikationen.',
     'users_details_desc_no_email' => 'Sætter et visningsnavn for denne bruger, så andre kan genkende dem.',
@@ -180,10 +180,10 @@ return [
     'users_delete_named' => 'Slet bruger :userName',
     'users_delete_warning' => 'Dette vil helt slette denne bruger med navnet \':userName\' fra systemet.',
     'users_delete_confirm' => 'Er du sikker på, at du vil slette denne bruger?',
-    'users_migrate_ownership' => 'Migrate Ownership',
-    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
-    'users_none_selected' => 'No user selected',
-    'users_delete_success' => 'User successfully removed',
+    'users_migrate_ownership' => 'Overfør ejerskab',
+    'users_migrate_ownership_desc' => 'Vælg en bruger her, hvis du vil have en anden bruger til at blive ejer af alle elementer, der i øjeblikket ejes af denne bruger.',
+    'users_none_selected' => 'Ingen bruger valgt',
+    'users_delete_success' => 'Brugeren blev fjernet',
     'users_edit' => 'Rediger bruger',
     'users_edit_profile' => 'Rediger profil',
     'users_edit_success' => 'Bruger suscesfuldt opdateret',
@@ -232,7 +232,7 @@ return [
         'ar' => 'العربية',
         'bg' => 'Bǎlgarski',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Catalansk',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'Hebraisk',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index d4564c571bc9acbce55f394d2cb3b85b0e43dc12..6c11f2e0fe4f3d938cd6b87a2903a6c3ec31c173 100644 (file)
@@ -89,7 +89,7 @@ return [
     'required_without'     => ':attribute skal udfyldes når :values ikke er udfyldt.',
     'required_without_all' => ':attribute skal udfyldes når ingen af :values er udfyldt.',
     'same'                 => ':attribute og :other skal være ens.',
-    'safe_url'             => 'The provided link may not be safe.',
+    'safe_url'             => 'Det angivne link kan være usikkert.',
     'size'                 => [
         'numeric' => ':attribute skal være :size.',
         'file'    => ':attribute skal være :size kilobytes.',
index 562a9add365decee4f8d5fe06b37dcd91f35dd0c..52c99168f2c20dfea41c309086d7e3d2d3a32579 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'hat das Bücherregal gelöscht',
     'bookshelf_delete_notification'    => 'Das Bücherregal wurde erfolgreich gelöscht',
 
+    // Favourites
+    'favourite_add_notification' => '":name" wurde zu deinen Favoriten hinzugefügt',
+    'favourite_remove_notification' => '":name" wurde aus Ihren Favoriten entfernt',
+
     // Other
     'commented_on'                => 'hat einen Kommentar hinzugefügt',
     'permissions_update'          => 'hat die Berechtigungen aktualisiert',
index a28c5bb0d87af331ce1c6e02278f24bc454c8d23..f46537ced82d1f0a850c3f3a30fe65a2b548b86c 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Entfernen',
     'add' => 'Hinzufügen',
     'fullscreen' => 'Vollbild',
+    'favourite' => 'Favorit',
+    'unfavourite' => 'Kein Favorit',
+    'next' => 'Nächste',
+    'previous' => 'Vorheriges',
 
     // Sort Options
     'sort_options' => 'Sortieroptionen',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Aufsteigend sortieren',
     'sort_descending' => 'Absteigend sortieren',
     'sort_name' => 'Name',
-    'sort_default' => 'Default',
+    'sort_default' => 'Standard',
     'sort_created_at' => 'Erstellungsdatum',
     'sort_updated_at' => 'Aktualisierungsdatum',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Brotkrumen',
 
     // Header
+    'header_menu_expand' => 'Header-Menü erweitern',
     'profile_menu' => 'Profilmenü',
     'view_profile' => 'Profil ansehen',
     'edit_profile' => 'Profil bearbeiten',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Sekundäre Informationen anzeigen',
     'tab_content' => 'Inhalt',
+    'tab_content_label' => 'Tab: Hauptinhalt anzeigen',
 
     // Email Content
     'email_action_help' => 'Sollte es beim Anklicken der Schaltfläche ":action_text" Probleme geben, öffnen Sie folgende URL in Ihrem Browser:',
index 3e82a4e9b51a9b861a9e6d6f52584f0344408278..d11c8dbaf1c8c6f53dad54f13fda9eec647e2660 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Bilder',
     'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
     'my_recently_viewed' => 'Kürzlich von mir angesehen',
+    'my_most_viewed_favourites' => 'Meine meistgesehenen Favoriten',
+    'my_favourites' => 'Meine Favoriten',
     'no_pages_viewed' => 'Sie haben bisher keine Seiten angesehen',
     'no_pages_recently_created' => 'Sie haben bisher keine Seiten angelegt',
     'no_pages_recently_updated' => 'Sie haben bisher keine Seiten aktualisiert',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Berechtigungen gesetzt',
     'search_created_by_me' => 'Von mir erstellt',
     'search_updated_by_me' => 'Von mir aktualisiert',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Besitzt von mir',
     'search_date_options' => 'Datums Optionen',
     'search_updated_before' => 'Aktualisiert vor',
     'search_updated_after' => 'Aktualisiert nach',
index 32be9b248f5933fe8942701363d12b3f15249a78..0a21857df001085189190dcf1304df634ae20d98 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Seite nicht gefunden',
     'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Sie angefordert haben, wurde nicht gefunden.',
     'sorry_page_not_found_permission_warning' => 'Wenn Sie erwartet haben, dass diese Seite existiert, haben Sie möglicherweise nicht die Berechtigung, sie anzuzeigen.',
+    'image_not_found' => 'Bild nicht gefunden',
+    'image_not_found_subtitle' => 'Entschuldigung. Das Bild, die Sie angefordert haben, wurde nicht gefunden.',
+    'image_not_found_details' => 'Wenn Sie erwartet haben, dass dieses Bild existiert, könnte es gelöscht worden sein.',
     'return_home' => 'Zurück zur Startseite',
     'error_occurred' => 'Es ist ein Fehler aufgetreten',
     'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
index 7fff1a75d64e085760825af4954280157e833401..a853a0b62ee1efc0ba768eb9474782fc9727616e 100644 (file)
@@ -40,7 +40,7 @@ Wenn Sie nicht eingeben, wird die Anwendung auf die Standardfarbe zurückgesetzt
     'app_homepage_desc' => 'Wählen Sie eine Seite als Startseite aus, die statt der Standardansicht angezeigt werden soll. Seitenberechtigungen werden für die ausgewählten Seiten ignoriert.',
     'app_homepage_select' => 'Wählen Sie eine Seite aus',
     'app_footer_links' => 'Fußzeilen-Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
+    'app_footer_links_desc' => 'Fügen Sie Links hinzu, die innerhalb der Seitenfußzeile angezeigt werden. Diese werden am unteren Ende der meisten Seiten angezeigt, einschließlich derjenigen, die keinen Login benötigen. Sie können die Bezeichnung "trans::<key>" verwenden, um systemdefinierte Übersetzungen zu verwenden. Beispiel: Mit "trans::common.privacy_policy" wird der übersetzte Text "Privacy Policy" bereitgestellt, und "trans::common.terms_of_service" liefert den übersetzten Text "Terms of Service".',
     'app_footer_links_label' => 'Link-Label',
     'app_footer_links_url' => 'Link-URL',
     'app_footer_links_add' => 'Fußzeilen-Link hinzufügen',
@@ -235,7 +235,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
         'ar' => 'العربية',
         'bg' => 'Bulgarisch',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Katalanisch',
         'cs' => 'Česky',
         'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
@@ -244,6 +244,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'Hebräisch',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 50bc29646fed9e5368a1739cfa4204f86326c1e6..fbaa9a211c0f8d38615f1d8f3de6cd8e57868424 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'löscht Bücherregal',
     'bookshelf_delete_notification'    => 'Das Bücherregal wurde erfolgreich gelöscht',
 
+    // Favourites
+    'favourite_add_notification' => '":name" wurde zu deinen Favoriten hinzugefügt',
+    'favourite_remove_notification' => '":name" wurde aus Ihren Favoriten entfernt',
+
     // Other
     'commented_on'                => 'kommentiert',
     'permissions_update'          => 'aktualisierte Berechtigungen',
index 793a56d462e14bd56444263ff5a8e9f192a0c54e..993d3e70c17dac3cbb53629db974888a753d6e83 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Entfernen',
     'add' => 'Hinzufügen',
     'fullscreen' => 'Vollbild',
+    'favourite' => 'Favorit',
+    'unfavourite' => 'Kein Favorit',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sortieroptionen',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Aufsteigend sortieren',
     'sort_descending' => 'Absteigend sortieren',
     'sort_name' => 'Name',
-    'sort_default' => 'Default',
+    'sort_default' => 'Standard',
     'sort_created_at' => 'Erstellungsdatum',
     'sort_updated_at' => 'Aktualisierungsdatum',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Brotkrumen',
 
     // Header
+    'header_menu_expand' => 'Header-Menü erweitern',
     'profile_menu' => 'Profilmenü',
     'view_profile' => 'Profil ansehen',
     'edit_profile' => 'Profil bearbeiten',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Sekundäre Informationen anzeigen',
     'tab_content' => 'Inhalt',
+    'tab_content_label' => 'Tab: Hauptinhalt anzeigen',
 
     // Email Content
     'email_action_help' => 'Sollte es beim Anklicken der Schaltfläche ":action_text" Probleme geben, öffne die folgende URL in Deinem Browser:',
@@ -81,6 +88,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Datenschutzbestimmungen',
+    'privacy_policy' => 'Datenschutzerklärung',
     'terms_of_service' => 'Allgemeine Geschäftsbedingungen',
 ];
index 3369831ac6b5f6c047ee6e6da07cce28d3a3e584..5377b1017ebbcc7db1fcf41f6d74e064b56cfdfe 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Bilder',
     'my_recent_drafts' => 'Meine kürzlichen Entwürfe',
     'my_recently_viewed' => 'Kürzlich von mir angesehen',
+    'my_most_viewed_favourites' => 'Meine meistgesehenen Favoriten',
+    'my_favourites' => 'Meine Favoriten',
     'no_pages_viewed' => 'Du hast bisher keine Seiten angesehen.',
     'no_pages_recently_created' => 'Du hast bisher keine Seiten angelegt.',
     'no_pages_recently_updated' => 'Du hast bisher keine Seiten aktualisiert.',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Berechtigungen gesetzt',
     'search_created_by_me' => 'Von mir erstellt',
     'search_updated_by_me' => 'Von mir aktualisiert',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Besitzt von mir',
     'search_date_options' => 'Datums Optionen',
     'search_updated_before' => 'Aktualisiert vor',
     'search_updated_after' => 'Aktualisiert nach',
index 656c3c23647133f1d12b38e0714bc68dd48f9a7c..181051459f5fd62f6d22f3f6c2190de93016a590 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Seite nicht gefunden',
     'sorry_page_not_found' => 'Entschuldigung. Die Seite, die Du angefordert hast, wurde nicht gefunden.',
     'sorry_page_not_found_permission_warning' => 'Wenn du erwartet hast, dass diese Seite existiert, hast du möglicherweise nicht die Berechtigung, sie anzuzeigen.',
+    'image_not_found' => 'Bild nicht gefunden',
+    'image_not_found_subtitle' => 'Entschuldigung. Das Bild, die Sie angefordert haben, wurde nicht gefunden.',
+    'image_not_found_details' => 'Wenn Sie erwartet haben, dass dieses Bild existiert, könnte es gelöscht worden sein.',
     'return_home' => 'Zurück zur Startseite',
     'error_occurred' => 'Es ist ein Fehler aufgetreten',
     'app_down' => ':appName befindet sich aktuell im Wartungsmodus.',
index 0315857f2a277865c4b649856d07573245800674..5c915c537db69f069acb447d6b8e5a6177a3de5c 100644 (file)
@@ -40,10 +40,10 @@ Wenn Du nichts eingibst, wird die Anwendung auf die Standardfarbe zurückgesetzt
     'app_homepage_desc' => 'Wähle eine Seite als Startseite aus, die statt der Standardansicht angezeigt werden soll. Seitenberechtigungen werden für die ausgewählten Seiten ignoriert.',
     'app_homepage_select' => 'Wählen Sie eine Seite aus',
     'app_footer_links' => 'Fußzeilen-Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
+    'app_footer_links_desc' => 'Fügen Sie Links hinzu, die innerhalb der Seitenfußzeile angezeigt werden. Diese werden am unteren Ende der meisten Seiten angezeigt, einschließlich derjenigen, die keinen Login benötigen. Sie können die Bezeichnung "trans::<key>" verwenden, um systemdefinierte Übersetzungen zu verwenden. Beispiel: Mit "trans::common.privacy_policy" wird der übersetzte Text "Privacy Policy" bereitgestellt, und "trans::common.terms_of_service" liefert den übersetzten Text "Terms of Service".',
     'app_footer_links_label' => 'Link-Label',
     'app_footer_links_url' => 'Link-URL',
-    'app_footer_links_add' => 'Fußzeilen-Link hinzufügen',
+    'app_footer_links_add' => 'Fußzeilenlink hinzufügen',
     'app_disable_comments' => 'Kommentare deaktivieren',
     'app_disable_comments_toggle' => 'Kommentare deaktivieren',
     'app_disable_comments_desc' => 'Deaktiviert Kommentare über alle Seiten in der Anwendung. Vorhandene Kommentare werden nicht angezeigt.',
@@ -235,7 +235,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
         'ar' => 'العربية',
         'bg' => 'Bulgarisch',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Katalanisch',
         'cs' => 'Česky',
         'da' => 'Dänisch',
         'de' => 'Deutsch (Sie)',
@@ -244,6 +244,7 @@ Hinweis: Benutzer können ihre E-Mail Adresse nach erfolgreicher Registrierung 
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index fe937b061930262a060465699446647adab763d9..5917de2cfb25745e4cda5a8046f310ec19da885b 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'deleted bookshelf',
     'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'commented on',
     'permissions_update'          => 'updated permissions',
index 3b003c64ad814ad97d36912c366850995c97488e..1861869e3ff7ac7d8ed69becf0e3f9aa2aef9a39 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
     'fullscreen' => 'Fullscreen',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -56,6 +60,7 @@ return [
     'no_activity' => 'No activity to show',
     'no_items' => 'No items available',
     'back_to_top' => 'Back to top',
+    'skip_to_main_content' => 'Skip to main content',
     'toggle_details' => 'Toggle Details',
     'toggle_thumbnails' => 'Toggle Thumbnails',
     'details' => 'Details',
@@ -65,6 +70,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'View Profile',
     'edit_profile' => 'Edit Profile',
@@ -73,7 +79,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Content',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
index 1661bae57cae5184af9381b15f79fe0c301d003a..1d4632bce352c632bbb066cfd36fbfac953db7da 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Images',
     'my_recent_drafts' => 'My Recent Drafts',
     'my_recently_viewed' => 'My Recently Viewed',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'You have not viewed any pages',
     'no_pages_recently_created' => 'No pages have been recently created',
     'no_pages_recently_updated' => 'No pages have been recently updated',
@@ -34,6 +36,7 @@ return [
     'export_html' => 'Contained Web File',
     'export_pdf' => 'PDF File',
     'export_text' => 'Plain Text File',
+    'export_md' => 'Markdown File',
 
     // Permissions and restrictions
     'permissions' => 'Permissions',
index 79024e482ed69efa633116592f9b7c83a0bcc93a..eb8ba54ea81506519a4958769d54086cc3b3d7be 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Page Not Found',
     'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
     'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Return to home',
     'error_occurred' => 'An Error Occurred',
     'app_down' => ':appName is down right now',
index 63d08e0e8ed41676228227023c5b33ac8d2a02b7..b04ea01cdcd638321b8828d72c7eca552eb0cb13 100755 (executable)
@@ -242,6 +242,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index b67781e18969774d39ed646fcabfb5c25335ea66..b64ada3a4c8f925f468e852942b77db1eb61a04e 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'estante eliminado',
     'bookshelf_delete_notification'    => 'Estante eliminado correctamente',
 
+    // Favourites
+    'favourite_add_notification' => '".name" ha sido añadido a sus favoritos',
+    'favourite_remove_notification' => '".name" ha sido eliminado de sus favoritos',
+
     // Other
     'commented_on'                => 'comentada el',
     'permissions_update'          => 'permisos actualizados',
index 5474ac9168e7563a25d03be12b27368a57e724eb..d19277402a858898eaa578aa6509c0a1321725b1 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remover',
     'add' => 'Añadir',
     'fullscreen' => 'Pantalla completa',
+    'favourite' => 'Añadir a favoritos',
+    'unfavourite' => 'Eliminar de favoritos',
+    'next' => 'Siguiente',
+    'previous' => 'Anterior',
 
     // Sort Options
     'sort_options' => 'Opciones de ordenación',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Ordenar Ascendentemente',
     'sort_descending' => 'Ordenar Descendentemente',
     'sort_name' => 'Nombre',
-    'sort_default' => 'Default',
+    'sort_default' => 'Predeterminada',
     'sort_created_at' => 'Fecha de Creación',
     'sort_updated_at' => 'Fecha de Modificación',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Rastro de migas de pan',
 
     // Header
+    'header_menu_expand' => 'Expandir el Menú de la Cabecera',
     'profile_menu' => 'Menú de Perfil',
     'view_profile' => 'Ver Perfil',
     'edit_profile' => 'Editar Perfil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Información',
+    'tab_info_label' => 'Pestaña: Mostrar Información Secundaria',
     'tab_content' => 'Contenido',
+    'tab_content_label' => 'Pestaña: Mostrar Contenido Primario',
 
     // Email Content
     'email_action_help' => 'Si está teniendo problemas clicando en el botón ":actionText", copie y pegue la siguiente URL en su navegador web:',
index 714f5b92890ae329ad9d216c6e6e593c376e33ab..2478d86893c26095eada6154901b6b28f70252a8 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Imágenes',
     'my_recent_drafts' => 'Mis borradores recientes',
     'my_recently_viewed' => 'Mis visualizaciones recientes',
+    'my_most_viewed_favourites' => 'Mis favoritos más vistos',
+    'my_favourites' => 'Mis favoritos',
     'no_pages_viewed' => 'No ha visto ninguna página',
     'no_pages_recently_created' => 'Ninguna página ha sido creada recientemente',
     'no_pages_recently_updated' => 'Ninguna página ha sido actualizada recientemente',
index f83ec4b80d77755158d06acbd5dc5d8cf2c25681..03e712526d4585b9fc4a17843f6ebcb03eff93d0 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Página no encontrada',
     'sorry_page_not_found' => 'Lo sentimos, la página a la que intenta acceder no pudo ser encontrada.',
     'sorry_page_not_found_permission_warning' => 'Si esperaba que esta página existiera, puede que no tenga permiso para verla.',
+    'image_not_found' => 'Imagen no encontrada',
+    'image_not_found_subtitle' => 'Lo sentimos, no se pudo encontrar el archivo de imagen que estaba buscando.',
+    'image_not_found_details' => 'Si esperaba que esta imagen existiera, podría haber sido eliminada.',
     'return_home' => 'Volver a la página de inicio',
     'error_occurred' => 'Ha ocurrido un error',
     'app_down' => 'La aplicación :appName se encuentra caída en este momento',
index d6a208b8c1da2aa261816ee2f29776b5466955a6..0010e7cae457d19328900d346bb9524b6649b35c 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 7c9e22450756469b593720860fe374a5542df9fc..2ee087b71fd7241f10667b3e98d1cbcb41dfbffa 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'Estante borrado',
     'bookshelf_delete_notification'    => 'Estante borrado exitosamente',
 
+    // Favourites
+    'favourite_add_notification' => '".name" se añadió a sus favoritos',
+    'favourite_remove_notification' => '".name" se eliminó de sus favoritos',
+
     // Other
     'commented_on'                => 'comentado',
     'permissions_update'          => 'permisos actualizados',
index 1d3a3d18d5a8d7041deba081c7c1d862aa1e1300..190d99c1ca7a5db4dd20555c4c991718d52568a3 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remover',
     'add' => 'Agregar',
     'fullscreen' => 'Pantalla completa',
+    'favourite' => 'Favoritos',
+    'unfavourite' => 'Eliminar de favoritos',
+    'next' => 'Siguiente',
+    'previous' => 'Anterior',
 
     // Sort Options
     'sort_options' => 'Opciones de Orden',
@@ -47,14 +51,14 @@ return [
     'sort_ascending' => 'Orden Ascendente',
     'sort_descending' => 'Orden Descendente',
     'sort_name' => 'Nombre',
-    'sort_default' => 'Default',
+    'sort_default' => 'Por defecto',
     'sort_created_at' => 'Fecha de creación',
     'sort_updated_at' => 'Fecha de actualización',
 
     // Misc
     'deleted_user' => 'Usuario borrado',
     'no_activity' => 'Ninguna actividad para mostrar',
-    'no_items' => 'No hay items disponibles',
+    'no_items' => 'No hay elementos disponibles',
     'back_to_top' => 'Volver arriba',
     'toggle_details' => 'Alternar detalles',
     'toggle_thumbnails' => 'Alternar miniaturas',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Miga de Pan',
 
     // Header
+    'header_menu_expand' => 'Expandir el Menú de Cabecera',
     'profile_menu' => 'Menu del Perfil',
     'view_profile' => 'Ver Perfil',
     'edit_profile' => 'Editar Perfil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Información',
+    'tab_info_label' => 'Pestaña: Mostrar Información Secundaria',
     'tab_content' => 'Contenido',
+    'tab_content_label' => 'Pestaña: Mostrar Contenido Primario',
 
     // Email Content
     'email_action_help' => 'Si está teniendo problemas haga click en el botón ":actionText", copie y pegue la siguiente URL en su navegador web:',
index 0862016565c2e1208b2971eff71e8b844688aa6d..02c2dcbb91907dacba82138c167ef618b692a623 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Imágenes',
     'my_recent_drafts' => 'Mis borradores recientes',
     'my_recently_viewed' => 'Mis visualizaciones recientes',
+    'my_most_viewed_favourites' => 'Mis Favoritos Más Vistos',
+    'my_favourites' => 'Mis Favoritos',
     'no_pages_viewed' => 'Ud. no ha visto ninguna página',
     'no_pages_recently_created' => 'Ninguna página ha sido creada recientemente',
     'no_pages_recently_updated' => 'Ninguna página ha sido actualizada recientemente',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Permisos establecidos',
     'search_created_by_me' => 'Creado por mí',
     'search_updated_by_me' => 'Actualizado por mí',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'De mi propiedad',
     'search_date_options' => 'Opciones de fecha',
     'search_updated_before' => 'Actualizado antes de',
     'search_updated_after' => 'Actualizado después de',
@@ -281,7 +283,7 @@ return [
     'attachments_link_attached' => 'Enlace agregado exitosamente a la página',
     'templates' => 'Plantillas',
     'templates_set_as_template' => 'La Página es una plantilla',
-    'templates_explain_set_as_template' => 'Puede establecer esta página como plantilla para que el contenido pueda utilizarse para al crear otras páginas. Otris usuarios podrán utilizar esta plantilla si tienen permisos para ver de esta página.',
+    'templates_explain_set_as_template' => 'Puede establecer esta página como plantilla para que el contenido pueda utilizarse al crear otras páginas. Otros usuarios podrán utilizar esta plantilla si tienen permisos para ver de esta página.',
     'templates_replace_content' => 'Reemplazar el contenido de la página',
     'templates_append_content' => 'Incorporar al fina del contenido de la página',
     'templates_prepend_content' => 'Incorporar al principio del contenido de la página',
index 41719a5bcb3c035c553eada751bd4e93eebf8a48..f96e29db148913798fbc4d181b297b093a796d7b 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Página no encontrada',
     'sorry_page_not_found' => 'Lo sentimos, la página que intenta acceder no pudo ser encontrada.',
     'sorry_page_not_found_permission_warning' => 'Si esperaba que esta página existiera, puede que no tenga permiso para verla.',
+    'image_not_found' => 'No se encuentra la imagen',
+    'image_not_found_subtitle' => 'Lo siento, no se pudo encontrar la imagen que busca.',
+    'image_not_found_details' => 'Si esperaba que esta imagen exista es probable que se haya eliminado.',
     'return_home' => 'Volver al home',
     'error_occurred' => 'Ha ocurrido un error',
     'app_down' => 'La aplicación :appName se encuentra caída en este momento',
index 0c3277ba9bd9f2f6860554c517bb31ee75d2d4b0..962ef89a4d213a8b964a16d9ec1f0349bc5559f4 100644 (file)
@@ -31,7 +31,7 @@ return [
     'app_custom_html_desc' => 'Cualquier contenido agregado aquí será agregado al final de la sección <head> de cada página. Esto es útil para sobreescribir estilos o agregar código para analíticas.',
     'app_custom_html_disabled_notice' => 'El contenido personailzado para la cabecera HTML está deshabilitado en esta configuración para garantizar que cualquier cambio importante se pueda revertir.',
     'app_logo' => 'Logo de la aplicación',
-    'app_logo_desc' => 'Esta imagen debería ser de 43px en altura. <br>Las imágenes grandes seán escaladas.',
+    'app_logo_desc' => 'Esta imagen debería ser de 43px en altura. <br>Las imágenes grandes serán achicadas.',
     'app_primary_color' => 'Color primario de la aplicación',
     'app_primary_color_desc' => 'Esto debería ser un valor hexadecimal. <br>Deje el valor vacío para reiniciar al valor por defecto.',
     'app_homepage' => 'Página de inicio de la Aplicación',
@@ -73,7 +73,7 @@ return [
     'maint' => 'Mantenimiento',
     'maint_image_cleanup' => 'Limpiar imágenes',
     'maint_image_cleanup_desc' => "Analizar contenido de páginas y revisiones para detectar cuáles imágenes y dibujos están en uso y cuáles son redundantes. Asegúrese de crear un respaldo completo de imágenes y base de datos antes de ejecutar esta tarea.",
-    'maint_delete_images_only_in_revisions' => 'Elimina también imágenes que sólo existen en antiguas revisiones de páginas',
+    'maint_delete_images_only_in_revisions' => 'También elimina imágenes que sólo existen en antiguas revisiones de páginas',
     'maint_image_cleanup_run' => 'Ejecutar limpieza',
     'maint_image_cleanup_warning' => 'Se encontraron :count imágenes pontencialmente sin uso. Está seguro de que quiere eliminarlas?',
     'maint_image_cleanup_success' => 'Se encontraron y se eliminaron :count imágenes pontencialmente sin uso!',
@@ -99,13 +99,13 @@ return [
     'recycle_bin_contents_empty' => 'La papelera de reciclaje está vacía',
     'recycle_bin_empty' => 'Vaciar Papelera de reciclaje',
     'recycle_bin_empty_confirm' => 'Esto destruirá permanentemente todos los elementos de la papelera de reciclaje, incluyendo el contenido existente en cada elemento. ¿Está seguro de que desea vaciar la papelera de reciclaje?',
-    'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido de nuevo. ¿Está seguro de que desea eliminar permanentemente este elemento?',
-    'recycle_bin_destroy_list' => 'Elementos a eliminar',
+    'recycle_bin_destroy_confirm' => 'Esta acción eliminará permanentemente este elemento del sistema, junto con los elementos secundarios listados a continuación, y no podrá restaurar este contenido. ¿Está seguro de que desea eliminar permanentemente este elemento?',
+    'recycle_bin_destroy_list' => 'Elementos a destruir',
     'recycle_bin_restore_list' => 'Elementos a restaurar',
     'recycle_bin_restore_confirm' => 'Esta acción restaurará el elemento eliminado, incluyendo cualquier elemento secundario, a su ubicación original. Si la ubicación original ha sido eliminada, y ahora está en la papelera de reciclaje, el elemento padre también tendrá que ser restaurado.',
     'recycle_bin_restore_deleted_parent' => 'El padre de este elemento también ha sido eliminado. Estos permanecerán eliminados hasta que el padre también sea restaurado.',
-    'recycle_bin_destroy_notification' => 'Eliminados :count artículos de la papelera de reciclaje.',
-    'recycle_bin_restore_notification' => 'Restaurados :count artículos desde la papelera de reciclaje.',
+    'recycle_bin_destroy_notification' => 'Eliminados :count elementos de la papelera de reciclaje.',
+    'recycle_bin_restore_notification' => 'Restaurados :count elementos desde la papelera de reciclaje.',
 
     // Audit Log
     'audit' => 'Registro de Auditoría',
@@ -183,8 +183,8 @@ return [
     'users_delete_confirm' => '¿Está seguro que desea borrar este usuario?',
     'users_migrate_ownership' => 'Cambiar Propietario',
     'users_migrate_ownership_desc' => 'Seleccione un usuario aquí si desea que otro usuario se convierta en el dueño de todos los elementos que actualmente son propiedad de este usuario.',
-    'users_none_selected' => 'Usuario no seleccionado',
-    'users_delete_success' => 'El usuario se ha eliminado correctamente',
+    'users_none_selected' => 'No hay usuario seleccionado',
+    'users_delete_success' => 'El usuario fue eliminado correctamente',
     'users_edit' => 'Editar Usuario',
     'users_edit_profile' => 'Editar perfil',
     'users_edit_success' => 'Usuario actualizado',
@@ -242,6 +242,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index fe937b061930262a060465699446647adab763d9..5917de2cfb25745e4cda5a8046f310ec19da885b 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'deleted bookshelf',
     'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'commented on',
     'permissions_update'          => 'updated permissions',
index 3b003c64ad814ad97d36912c366850995c97488e..d4508c3c7d4c9a7def3b457b1d9d2d96673d7d23 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remove',
     'add' => 'Add',
     'fullscreen' => 'Fullscreen',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'View Profile',
     'edit_profile' => 'Edit Profile',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Content',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'If you’re having trouble clicking the ":actionText" button, copy and paste the URL below into your web browser:',
index 1661bae57cae5184af9381b15f79fe0c301d003a..462402f33f407b458700e80e2c15f22257ceba52 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Images',
     'my_recent_drafts' => 'My Recent Drafts',
     'my_recently_viewed' => 'My Recently Viewed',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'You have not viewed any pages',
     'no_pages_recently_created' => 'No pages have been recently created',
     'no_pages_recently_updated' => 'No pages have been recently updated',
index 79024e482ed69efa633116592f9b7c83a0bcc93a..eb8ba54ea81506519a4958769d54086cc3b3d7be 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Page Not Found',
     'sorry_page_not_found' => 'Sorry, The page you were looking for could not be found.',
     'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Return to home',
     'error_occurred' => 'An Error Occurred',
     'app_down' => ':appName is down right now',
index ab7f153224afd41bb90e9cd54a9472895727b619..8a7946b1281269a0f62d17d6dacea2afd466f56b 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 8b62bd0528a390770f9e1fe6b9694ef88867f317..05f7410097627b7fc3df8140a96fac105a642201 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'a supprimé l\'étagère',
     'bookshelf_delete_notification'    => 'Étagère supprimée avec succès',
 
+    // Favourites
+    'favourite_add_notification' => '":name" a été ajouté dans vos favoris',
+    'favourite_remove_notification' => '":name" a été supprimé de vos favoris',
+
     // Other
     'commented_on'                => 'a commenté',
     'permissions_update'          => 'mettre à jour les autorisations',
index d97238744f6babaab58030a6253746a367b18e69..6203c7326cafeb75deadd684cd5078d079820c2d 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Enlever',
     'add' => 'Ajouter',
     'fullscreen' => 'Plein écran',
+    'favourite' => 'Favoris',
+    'unfavourite' => 'Supprimer des favoris',
+    'next' => 'Suivant',
+    'previous' => 'Précédent',
 
     // Sort Options
     'sort_options' => 'Options de tri',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Tri ascendant',
     'sort_descending' => 'Tri descendant',
     'sort_name' => 'Nom',
-    'sort_default' => 'Default',
+    'sort_default' => 'Défaut',
     'sort_created_at' => 'Date de création',
     'sort_updated_at' => 'Date de mise à jour',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Fil d\'Ariane',
 
     // Header
+    'header_menu_expand' => 'Développer le menu',
     'profile_menu' => 'Menu du profil',
     'view_profile' => 'Voir le profil',
     'edit_profile' => 'Modifier le profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informations',
+    'tab_info_label' => 'Onglet : Afficher les informations secondaires',
     'tab_content' => 'Contenu',
+    'tab_content_label' => 'Onglet : Afficher le contenu principal',
 
     // Email Content
     'email_action_help' => 'Si vous rencontrez des problèmes pour cliquer sur le bouton ":actionText", copiez et collez l\'adresse ci-dessous dans votre navigateur :',
index 092031f9dc797608bc4ea7bc7144c527b5d1f359..507d630e1b272d4d884a092eab4cb9ce849d8e84 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Images',
     'my_recent_drafts' => 'Mes brouillons récents',
     'my_recently_viewed' => 'Vus récemment',
+    'my_most_viewed_favourites' => 'Mes Favoris les plus vus',
+    'my_favourites' => 'Mes favoris',
     'no_pages_viewed' => 'Vous n\'avez rien visité récemment',
     'no_pages_recently_created' => 'Aucune page créée récemment',
     'no_pages_recently_updated' => 'Aucune page mise à jour récemment',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Ensemble d\'autorisations',
     'search_created_by_me' => 'Créé par moi',
     'search_updated_by_me' => 'Mis à jour par moi',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Créés par moi',
     'search_date_options' => 'Recherche par date',
     'search_updated_before' => 'Mis à jour avant',
     'search_updated_after' => 'Mis à jour après',
index c1c14fe31f36ed8313026824792f19e2577cda8f..511683285649e13aa9177451af9b85f8655400f2 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Page non trouvée',
     'sorry_page_not_found' => 'Désolé, cette page n\'a pas pu être trouvée.',
     'sorry_page_not_found_permission_warning' => 'Si vous vous attendiez à ce que cette page existe, il se peut que vous n\'ayez pas l\'autorisation de la consulter.',
+    'image_not_found' => 'Image non trouvée',
+    'image_not_found_subtitle' => 'Désolé, l\'image que vous cherchez ne peut être trouvée.',
+    'image_not_found_details' => 'Si vous vous attendiez à ce que cette image existe, elle pourrait avoir été supprimée.',
     'return_home' => 'Retour à l\'accueil',
     'error_occurred' => 'Une erreur est survenue',
     'app_down' => ':appName n\'est pas en service pour le moment',
index fb525cec28553bfa04cc836e48971739669b3dc0..ab1febc65d92534e46716bfbab10633b3546cb5f 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'Hébreu',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 0babc38d17ed94ff4f2c7034983f230d3bda7fcd..dfa54344281b87a35cf2c31b91f39c844e3ca615 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'deleted bookshelf',
     'bookshelf_delete_notification'    => 'מדף הספרים הוסר בהצלחה',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'commented on',
     'permissions_update'          => 'updated permissions',
index 89eae87f4092eccb9f4723e5d089da990e353154..3ab175bae7e438b7dfb4b3abfe3e6b53e512e19b 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'הסר',
     'add' => 'הוסף',
     'fullscreen' => 'Fullscreen',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'הצג פרופיל',
     'edit_profile' => 'ערוך פרופיל',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'מידע',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'תוכן',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'אם לא ניתן ללחות על כפתור ״:actionText״, יש להעתיק ולהדביק את הכתובת למטה אל דפדפן האינטרנט שלך:',
index c8d239bca6cf711a365a1a4e1e69f44ba7e9ba57..a0b4918aee12e8dc2b81064f12cd545ea8eb5fe7 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'תמונות',
     'my_recent_drafts' => 'הטיוטות האחרונות שלי',
     'my_recently_viewed' => 'הנצפים לאחרונה שלי',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'לא צפית בדפים כלשהם',
     'no_pages_recently_created' => 'לא נוצרו דפים לאחרונה',
     'no_pages_recently_updated' => 'לא עודכנו דפים לאחרונה',
index f6a01d3a3ff4d9c94ddc1c2ec980793cb0b63530..5c879216c234006ce0480904a0bb3a868ccc8768 100644 (file)
@@ -83,6 +83,9 @@ return [
     '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.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'בחזרה לדף הבית',
     'error_occurred' => 'התרחשה שגיאה',
     'app_down' => ':appName כרגע אינו זמין',
index b7ce527693a0cbc986ce3a36d01b59c9b3f888c0..a648e2e0203b8016fb9a7920c048066ed9a23573 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
diff --git a/resources/lang/hr/activities.php b/resources/lang/hr/activities.php
new file mode 100644 (file)
index 0000000..a3340b4
--- /dev/null
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Activity text strings.
+ * Is used for all the text within activity logs & notifications.
+ */
+return [
+
+    // Pages
+    'page_create'                 => 'stvorena stranica',
+    'page_create_notification'    => 'Stranica je uspješno stvorena',
+    'page_update'                 => 'ažurirana stranica',
+    'page_update_notification'    => 'Stranica je uspješno ažurirana',
+    'page_delete'                 => 'izbrisana stranica',
+    'page_delete_notification'    => 'Stranica je uspješno izbrisana',
+    'page_restore'                => 'obnovljena stranica',
+    'page_restore_notification'   => 'Stranica je uspješno obnovljena',
+    'page_move'                   => 'premještena stranica',
+
+    // Chapters
+    'chapter_create'              => 'stvoreno poglavlje',
+    'chapter_create_notification' => 'Poglavlje je uspješno stvoreno',
+    'chapter_update'              => 'ažurirano poglavlje',
+    'chapter_update_notification' => 'Poglavlje je uspješno ažurirano',
+    'chapter_delete'              => 'izbrisano poglavlje',
+    'chapter_delete_notification' => 'Poglavlje je uspješno izbrisano',
+    'chapter_move'                => 'premiješteno poglavlje',
+
+    // Books
+    'book_create'                 => 'stvorena knjiga',
+    'book_create_notification'    => 'Knjiga je uspješno stvorena',
+    'book_update'                 => 'ažurirana knjiga',
+    'book_update_notification'    => 'Knjiga je uspješno ažurirana',
+    'book_delete'                 => 'izbrisana knjiga',
+    'book_delete_notification'    => 'Knjiga je uspješno izbrisana',
+    'book_sort'                   => 'razvrstana knjiga',
+    'book_sort_notification'      => 'Knjiga je uspješno razvrstana',
+
+    // Bookshelves
+    'bookshelf_create'            => 'stvorena polica za knjige',
+    'bookshelf_create_notification'    => 'Polica za knjige je uspješno stvorena',
+    'bookshelf_update'                 => 'ažurirana polica za knjige',
+    'bookshelf_update_notification'    => 'Polica za knjige je uspješno ažurirana',
+    'bookshelf_delete'                 => 'izbrisana polica za knjige',
+    'bookshelf_delete_notification'    => 'Polica za knjige je uspješno izbrisana',
+
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
+    // Other
+    'commented_on'                => 'komentirano',
+    'permissions_update'          => 'ažurirana dopuštenja',
+];
diff --git a/resources/lang/hr/auth.php b/resources/lang/hr/auth.php
new file mode 100644 (file)
index 0000000..9680cc7
--- /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' => 'Ove vjerodajnice ne podudaraju se s našim zapisima.',
+    'throttle' => 'Previše pokušaja prijave. Molimo vas da pokušate za :seconds sekundi.',
+
+    // Login & Register
+    'sign_up' => 'Registrirajte se',
+    'log_in' => 'Prijavite se',
+    'log_in_with' => 'Prijavite se sa :socialDriver',
+    'sign_up_with' => 'Registrirajte se sa :socialDriver',
+    'logout' => 'Odjavite se',
+
+    'name' => 'Ime',
+    'username' => 'Korisničko ime',
+    'email' => 'Email',
+    'password' => 'Lozinka',
+    'password_confirm' => 'Potvrdite lozinku',
+    'password_hint' => 'Mora imati više od 7 znakova',
+    'forgot_password' => 'Zaboravili ste lozinku?',
+    'remember_me' => 'Zapamti me',
+    'ldap_email_hint' => 'Molimo upišite mail korišten za ovaj račun.',
+    'create_account' => 'Stvori račun',
+    'already_have_account' => 'Imate li već račun?',
+    'dont_have_account' => 'Nemate račun?',
+    'social_login' => 'Social Login',
+    'social_registration' => 'Social Registration',
+    'social_registration_text' => 'Prijavite se putem drugih servisa.',
+
+    'register_thanks' => 'Zahvaljujemo na registraciji!',
+    'register_confirm' => 'Molimo, provjerite svoj email i kliknite gumb za potvrdu pristupa :appName.',
+    'registrations_disabled' => 'Registracije su trenutno onemogućene',
+    'registration_email_domain_invalid' => 'Ova e-mail adresa se ne može koristiti u ovoj aplikaciji',
+    'register_success' => 'Hvala na prijavi! Sada ste registrirani i prijavljeni.',
+
+
+    // Password Reset
+    'reset_password' => 'Promijenite lozinku',
+    'reset_password_send_instructions' => 'Upišite svoju e-mail adresu kako biste primili poveznicu za promjenu lozinke.',
+    'reset_password_send_button' => 'Pošalji poveznicu za promjenu lozinke',
+    'reset_password_sent' => 'Poveznica za promjenu lozinke poslat će se na :email adresu ako je u našem sustavu.',
+    'reset_password_success' => 'Vaša lozinka je uspješno promijenjena.',
+    'email_reset_subject' => 'Promijenite svoju :appName lozinku',
+    'email_reset_text' => 'Primili ste ovu poruku jer je zatražena promjena lozinke za vaš račun.',
+    'email_reset_not_requested' => 'Ako niste tražili promjenu lozinke slobodno zanemarite ovu poruku.',
+
+
+    // Email Confirmation
+    'email_confirm_subject' => 'Potvrdite svoju e-mail adresu na :appName',
+    'email_confirm_greeting' => 'Hvala na prijavi :appName!',
+    'email_confirm_text' => 'Molimo potvrdite svoju e-mail adresu klikom na donji gumb.',
+    'email_confirm_action' => 'Potvrdi Email',
+    'email_confirm_send_error' => 'Potvrda e-mail adrese je obavezna, ali sustav ne može poslati e-mail. Javite se administratoru kako bi provjerio vaš e-mail.',
+    'email_confirm_success' => 'Vaš e-mail adresa je potvrđena!',
+    'email_confirm_resent' => 'Ponovno je poslana potvrda. Molimo, provjerite svoj inbox.',
+
+    'email_not_confirmed' => 'E-mail adresa nije potvrđena.',
+    'email_not_confirmed_text' => 'Vaša e-mail adresa još nije potvrđena.',
+    'email_not_confirmed_click_link' => 'Molimo, kliknite na poveznicu koju ste primili kratko nakon registracije.',
+    'email_not_confirmed_resend' => 'Ako ne možete pronaći e-mail za postavljanje lozinke možete ga zatražiti ponovno ispunjavanjem ovog obrasca.',
+    'email_not_confirmed_resend_button' => 'Ponovno pošalji e-mail potvrde',
+
+    // User Invite
+    'user_invite_email_subject' => 'Pozvani ste pridružiti se :appName!',
+    'user_invite_email_greeting' => 'Vaš račun je kreiran za vas na :appName',
+    'user_invite_email_text' => 'Kliknite ispod da biste postavili račun i dobili pristup.',
+    'user_invite_email_action' => 'Postavite lozinku',
+    'user_invite_page_welcome' => 'Dobrodošli u :appName!',
+    'user_invite_page_text' => 'Da biste postavili račun i dobili pristup trebate unijeti lozinku kojom ćete se ubuduće prijaviti na :appName.',
+    'user_invite_page_confirm_button' => 'Potvrdite lozinku',
+    'user_invite_success' => 'Lozinka je postavljena, možete pristupiti :appName!'
+];
\ No newline at end of file
diff --git a/resources/lang/hr/common.php b/resources/lang/hr/common.php
new file mode 100644 (file)
index 0000000..1df3c22
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+/**
+ * Common elements found throughout many areas of BookStack.
+ */
+return [
+
+    // Buttons
+    'cancel' => 'Odustani',
+    'confirm' => 'Potvrdi',
+    'back' => 'Natrag',
+    'save' => 'Spremi',
+    'continue' => 'Nastavi',
+    'select' => 'Odaberi',
+    'toggle_all' => 'Prebaci sve',
+    'more' => 'Više',
+
+    // Form Labels
+    'name' => 'Ime',
+    'description' => 'Opis',
+    'role' => 'Uloga',
+    'cover_image' => 'Naslovna slika',
+    'cover_image_description' => 'Slika treba biti približno 440x250px.',
+    
+    // Actions
+    'actions' => 'Aktivnost',
+    'view' => 'Pogled',
+    'view_all' => 'Pogledaj sve',
+    'create' => 'Stvori',
+    'update' => 'Ažuriraj',
+    'edit' => 'Uredi',
+    'sort' => 'Razvrstaj',
+    'move' => 'Makni',
+    'copy' => 'Kopiraj',
+    'reply' => 'Ponovi',
+    'delete' => 'Izbriši',
+    'delete_confirm' => 'Potvrdite brisanje',
+    'search' => 'Traži',
+    'search_clear' => 'Očisti pretragu',
+    'reset' => 'Ponovno postavi',
+    'remove' => 'Ukloni',
+    'add' => 'Dodaj',
+    'fullscreen' => 'Cijeli zaslon',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
+
+    // Sort Options
+    'sort_options' => 'Razvrstaj opcije',
+    'sort_direction_toggle' => 'Razvrstaj smjer prebacivanja',
+    'sort_ascending' => 'Razvrstaj uzlazno',
+    'sort_descending' => 'Razvrstaj silazno',
+    'sort_name' => 'Ime',
+    'sort_default' => 'Zadano',
+    'sort_created_at' => 'Datum',
+    'sort_updated_at' => 'Ažuriraj datum',
+
+    // Misc
+    'deleted_user' => 'Izbrisani korisnik',
+    'no_activity' => 'Nema aktivnosti za pregled',
+    'no_items' => 'Nedostupno',
+    'back_to_top' => 'Natrag na vrh',
+    'toggle_details' => 'Prebaci detalje',
+    'toggle_thumbnails' => 'Uključi minijature',
+    'details' => 'Detalji',
+    'grid_view' => 'Prikaz rešetke',
+    'list_view' => 'Prikaz popisa',
+    'default' => 'Zadano',
+    'breadcrumb' => 'Breadcrumb',
+
+    // Header
+    'header_menu_expand' => 'Proširi izbornik',
+    'profile_menu' => 'Profil',
+    'view_profile' => 'Vidi profil',
+    'edit_profile' => 'Uredite profil',
+    'dark_mode' => 'Tamni način',
+    'light_mode' => 'Svijetli način',
+
+    // Layout tabs
+    'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: pokaži sekundarne informacije',
+    'tab_content' => 'Sadržaj',
+    'tab_content_label' => 'Tab: pokaži primarni sadržaj',
+
+    // Email Content
+    'email_action_help' => 'Ako imate poteškoća s klikom na gumb ":actionText", kopirajte i zalijepite donji URL u vaš preglednik.',
+    'email_rights' => 'Sva prava pridržana',
+
+    // Footer Link Options
+    // Not directly used but available for convenience to users.
+    'privacy_policy' => 'Politika privatnosti',
+    'terms_of_service' => 'Uvjeti korištenja',
+];
diff --git a/resources/lang/hr/components.php b/resources/lang/hr/components.php
new file mode 100644 (file)
index 0000000..5caffd5
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+/**
+ * Text used in custom JavaScript driven components.
+ */
+return [
+
+    // Image Manager
+    'image_select' => 'Odabir slike',
+    'image_all' => 'Sve',
+    'image_all_title' => 'Vidi sve slike',
+    'image_book_title' => 'Vidi slike dodane ovoj knjizi',
+    'image_page_title' => 'Vidi slike dodane ovoj stranici',
+    'image_search_hint' => 'Pretraži pomoću imena slike',
+    'image_uploaded' => 'Učitano :uploadedDate',
+    'image_load_more' => 'Učitaj više',
+    'image_image_name' => 'Ime slike',
+    'image_delete_used' => 'Ova slika korištena je na donjoj stranici.',
+    'image_delete_confirm_text' => 'Jeste li sigurni da želite obrisati sliku?',
+    'image_select_image' => 'Odaberi sliku',
+    'image_dropzone' => 'Prebacite sliku ili kliknite ovdje za prijenos',
+    'images_deleted' => 'Obrisane slike',
+    'image_preview' => 'Pregled slike',
+    'image_upload_success' => 'Slika je uspješno dodana',
+    'image_update_success' => 'Detalji slike su uspješno ažurirani',
+    'image_delete_success' => 'Slika je obrisana',
+    'image_upload_remove' => 'Ukloni',
+
+    // Code Editor
+    'code_editor' => 'Uredi kod',
+    'code_language' => 'Jezik koda',
+    'code_content' => 'Sadržaj koda',
+    'code_session_history' => 'Povijest sesije',
+    'code_save' => 'Spremi kod',
+];
diff --git a/resources/lang/hr/entities.php b/resources/lang/hr/entities.php
new file mode 100644 (file)
index 0000000..e8a8004
--- /dev/null
@@ -0,0 +1,322 @@
+<?php
+/**
+ * Text used for 'Entities' (Document Structure Elements) such as
+ * Books, Shelves, Chapters & Pages
+ */
+return [
+
+    // Shared
+    'recently_created' => 'Nedavno stvoreno',
+    'recently_created_pages' => 'Nedavno stvorene stranice',
+    'recently_updated_pages' => 'Nedavno ažurirane stranice',
+    'recently_created_chapters' => 'Nedavno stvorena poglavlja',
+    'recently_created_books' => 'Nedavno stvorene knjige',
+    'recently_created_shelves' => 'Nedavno stvorene police',
+    'recently_update' => 'Nedavno ažurirano',
+    'recently_viewed' => 'Nedavno viđeno',
+    'recent_activity' => 'Nedavna aktivnost',
+    'create_now' => 'Stvori sada',
+    'revisions' => 'Revizije',
+    'meta_revision' => 'Revizija #:revisionCount',
+    'meta_created' => 'Stvoreno :timeLength',
+    'meta_created_name' => 'Stvoreno :timeLength od :user',
+    'meta_updated' => 'Ažurirano :timeLength',
+    'meta_updated_name' => 'Ažurirano :timeLength od :user',
+    'meta_owned_name' => 'Vlasništvo :user',
+    'entity_select' => 'Odaberi subjekt',
+    'images' => 'Slike',
+    'my_recent_drafts' => 'Nedavne skice',
+    'my_recently_viewed' => 'Nedavno viđeno',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
+    'no_pages_viewed' => 'Niste pogledali nijednu stranicu',
+    'no_pages_recently_created' => 'Nema nedavno stvorenih stranica',
+    'no_pages_recently_updated' => 'Nema nedavno ažuriranih stranica',
+    'export' => 'Izvoz',
+    'export_html' => 'Web File',
+    'export_pdf' => 'PDF File',
+    'export_text' => 'Text File',
+
+    // Permissions and restrictions
+    'permissions' => 'Dopuštenja',
+    'permissions_intro' => 'Jednom postavljene, ove dozvole bit će prioritetne ostalim dopuštenjima.',
+    'permissions_enable' => 'Omogući dopuštenje za korištenje',
+    'permissions_save' => 'Spremi dopuštenje',
+    'permissions_owner' => 'Vlasnik',
+
+    // Search
+    'search_results' => 'Pretraži rezultate',
+    'search_total_results_found' => ':count rezultat|:count ukupno pronađenih rezultata',
+    'search_clear' => 'Očisti pretragu',
+    'search_no_pages' => 'Nijedna stranica ne podudara se s ovim pretraživanjem',
+    'search_for_term' => 'Traži :term',
+    'search_more' => 'Više rezultata',
+    'search_advanced' => 'Napredno pretraživanje',
+    'search_terms' => 'Pretraži pojmove',
+    'search_content_type' => 'Vrsta sadržaja',
+    'search_exact_matches' => 'Podudarnosti',
+    'search_tags' => 'Označi pretragu',
+    'search_options' => 'Opcije',
+    'search_viewed_by_me' => 'Pregledano od mene',
+    'search_not_viewed_by_me' => 'Nije pregledano od mene',
+    'search_permissions_set' => 'Set dopuštenja',
+    'search_created_by_me' => 'Stvoreno od mene',
+    'search_updated_by_me' => 'Ažurirano od mene',
+    'search_owned_by_me' => 'Moje vlasništvo',
+    'search_date_options' => 'Opcije datuma',
+    'search_updated_before' => 'Ažurirano prije',
+    'search_updated_after' => 'Ažurirano nakon',
+    'search_created_before' => 'Stvoreno prije',
+    'search_created_after' => 'Stvoreno nakon',
+    'search_set_date' => 'Datumi',
+    'search_update' => 'Ažuriraj pretragu',
+
+    // Shelves
+    'shelf' => 'Polica',
+    'shelves' => 'Police',
+    'x_shelves' => ':count polica|:count polica',
+    'shelves_long' => 'Police za knjige',
+    'shelves_empty' => 'Nijedna polica nije stvorena',
+    'shelves_create' => 'Stvori novu policu',
+    'shelves_popular' => 'Popularne police',
+    'shelves_new' => 'Nove police',
+    'shelves_new_action' => 'Nova polica',
+    'shelves_popular_empty' => 'Najpopularnije police pojavit će se. ovdje.',
+    'shelves_new_empty' => 'Nedavno stvorene police pojavit će se ovdje.',
+    'shelves_save' => 'Spremi policu',
+    'shelves_books' => 'Knjige na ovoj polici',
+    'shelves_add_books' => 'Dodaj knjige na ovu policu',
+    'shelves_drag_books' => 'Prebaci knjige na ovu policu',
+    'shelves_empty_contents' => 'Ova polica još nema dodijeljene knjige',
+    'shelves_edit_and_assign' => 'Uredi policu za dodavanje knjiga',
+    'shelves_edit_named' => 'Uredi policu :name',
+    'shelves_edit' => 'Uredi policu',
+    'shelves_delete' => 'Izbriši policu',
+    'shelves_delete_named' => 'Izbriši policu :name',
+    'shelves_delete_explain' => "This will delete the bookshelf with the name ':name'. Contained books will not be deleted.",
+    'shelves_delete_confirmation' => 'Jeste li sigurni da želite obrisati policu?',
+    'shelves_permissions' => 'Dopuštenja za policu',
+    'shelves_permissions_updated' => 'Ažurirana dopuštenja za policu',
+    'shelves_permissions_active' => 'Aktivirana dopuštenja za policu',
+    'shelves_copy_permissions_to_books' => 'Kopiraj dopuštenja za knjige',
+    'shelves_copy_permissions' => 'Kopiraj dopuštenja',
+    'shelves_copy_permissions_explain' => 'Ovo će promijeniti trenutna dopuštenja za policu i knjige u njoj. Prije aktivacije provjerite jesu li sve dopuštenja za ovu policu spremljena.',
+    'shelves_copy_permission_success' => 'Dopuštenja za policu kopirana za :count knjiga',
+
+    // Books
+    'book' => 'Knjiga',
+    'books' => 'Knjige',
+    'x_books' => ':count knjiga|:count knjiga',
+    'books_empty' => 'Nijedna knjiga nije stvorena',
+    'books_popular' => 'Popularne knjige',
+    'books_recent' => 'Nedavne knjige',
+    'books_new' => 'Nove knjige',
+    'books_new_action' => 'Nova knjiga',
+    'books_popular_empty' => 'Najpopularnije knjige pojavit će se ovdje.',
+    'books_new_empty' => 'Najnovije knjige pojavit će se ovdje.',
+    'books_create' => 'Stvori novu knjigu',
+    'books_delete' => 'Izbriši knjigu',
+    'books_delete_named' => 'Izbriši knjigu :bookName',
+    'books_delete_explain' => 'Ovaj korak će izbrisati knjigu \':bookName\'. Izbrisati će sve stranice i poglavlja.',
+    'books_delete_confirmation' => 'Jeste li sigurni da želite izbrisati ovu knjigu?',
+    'books_edit' => 'Uredi knjigu',
+    'books_edit_named' => 'Uredi knjigu :bookName',
+    'books_form_book_name' => 'Ime knjige',
+    'books_save' => 'Spremi knjigu',
+    'books_permissions' => 'Dopuštenja za knjigu',
+    'books_permissions_updated' => 'Ažurirana dopuštenja za knjigu',
+    'books_empty_contents' => 'U ovoj knjizi još nema stranica ni poglavlja.',
+    'books_empty_create_page' => 'Stvori novu stranicu',
+    'books_empty_sort_current_book' => 'Razvrstaj postojeće knjige',
+    'books_empty_add_chapter' => 'Dodaj poglavlje',
+    'books_permissions_active' => 'Aktivna dopuštenja za knjigu',
+    'books_search_this' => 'Traži knjigu',
+    'books_navigation' => 'Navigacija knjige',
+    'books_sort' => 'Razvrstaj sadržaj knjige',
+    'books_sort_named' => 'Razvrstaj knjigu :bookName',
+    'books_sort_name' => 'Razvrstaj po imenu',
+    'books_sort_created' => 'Razvrstaj po datumu nastanka',
+    'books_sort_updated' => 'Razvrstaj po datumu ažuriranja',
+    'books_sort_chapters_first' => 'Prva poglavlja',
+    'books_sort_chapters_last' => 'Zadnja poglavlja',
+    'books_sort_show_other' => 'Pokaži ostale knjige',
+    'books_sort_save' => 'Spremi novi poredak',
+
+    // Chapters
+    'chapter' => 'Poglavlje',
+    'chapters' => 'Poglavlja',
+    'x_chapters' => ':count poglavlje|:count poglavlja',
+    'chapters_popular' => 'Popularna poglavlja',
+    'chapters_new' => 'Novo poglavlje',
+    'chapters_create' => 'Stvori novo poglavlje',
+    'chapters_delete' => 'Izbriši poglavlje',
+    'chapters_delete_named' => 'Izbriši poglavlje :chapterName',
+    'chapters_delete_explain' => 'Ovaj korak briše poglavlje \':chapterName\'. Sve stranice u njemu će biti izbrisane.',
+    'chapters_delete_confirm' => 'Jeste li sigurni da želite izbrisati poglavlje?',
+    'chapters_edit' => 'Uredi poglavlje',
+    'chapters_edit_named' => 'Uredi poglavlje :chapterName',
+    'chapters_save' => 'Spremi poglavlje',
+    'chapters_move' => 'Premjesti poglavlje',
+    'chapters_move_named' => 'Premjesti poglavlje :chapterName',
+    'chapter_move_success' => 'Poglavlje premješteno u :bookName',
+    'chapters_permissions' => 'Dopuštenja za poglavlje',
+    'chapters_empty' => 'U ovom poglavlju nema stranica.',
+    'chapters_permissions_active' => 'Aktivna dopuštenja za poglavlje',
+    'chapters_permissions_success' => 'Ažurirana dopuštenja za poglavlje',
+    'chapters_search_this' => 'Pretraži poglavlje',
+
+    // Pages
+    'page' => 'Stranica',
+    'pages' => 'Stranice',
+    'x_pages' => ':count stranice|:count stranica',
+    'pages_popular' => 'Popularne stranice',
+    'pages_new' => 'Nova stranica',
+    'pages_attachments' => 'Prilozi',
+    'pages_navigation' => 'Navigacija stranice',
+    'pages_delete' => 'Izbriši stranicu',
+    'pages_delete_named' => 'Izbriši stranicu :pageName',
+    'pages_delete_draft_named' => 'Izbriši nacrt stranice :pageName',
+    'pages_delete_draft' => 'Izbriši nacrt stranice',
+    'pages_delete_success' => 'Izbrisana stranica',
+    'pages_delete_draft_success' => 'Izbrisan nacrt stranice',
+    'pages_delete_confirm' => 'Jeste li sigurni da želite izbrisati stranicu?',
+    'pages_delete_draft_confirm' => 'Jeste li sigurni da želite izbrisati nacrt stranice?',
+    'pages_editing_named' => 'Uređivanje stranice :pageName',
+    'pages_edit_draft_options' => 'Izrada skice',
+    'pages_edit_save_draft' => 'Spremi nacrt',
+    'pages_edit_draft' => 'Uredi nacrt stranice',
+    'pages_editing_draft' => 'Uređivanja nacrta',
+    'pages_editing_page' => 'Uređivanje stranice',
+    'pages_edit_draft_save_at' => 'Nacrt spremljen kao',
+    'pages_edit_delete_draft' => 'Izbriši nacrt',
+    'pages_edit_discard_draft' => 'Odbaci nacrt',
+    'pages_edit_set_changelog' => 'Postavi dnevnik promjena',
+    'pages_edit_enter_changelog_desc' => 'Ukratko opišite promjene koje ste napravili',
+    'pages_edit_enter_changelog' => 'Unesi dnevnik promjena',
+    'pages_save' => 'Spremi stranicu',
+    'pages_title' => 'Naslov stranice',
+    'pages_name' => 'Ime stranice',
+    'pages_md_editor' => 'Uređivač',
+    'pages_md_preview' => 'Pregled',
+    'pages_md_insert_image' => 'Umetni sliku',
+    'pages_md_insert_link' => 'Umetni poveznicu',
+    'pages_md_insert_drawing' => 'Umetni crtež',
+    'pages_not_in_chapter' => 'Stranica nije u poglavlju',
+    'pages_move' => 'Premjesti stranicu',
+    'pages_move_success' => 'Stranica premještena u ":parentName"',
+    'pages_copy' => 'Kopiraj stranicu',
+    'pages_copy_desination' => 'Kopiraj odredište',
+    'pages_copy_success' => 'Stranica je uspješno kopirana',
+    'pages_permissions' => 'Dopuštenja stranice',
+    'pages_permissions_success' => 'Ažurirana dopuštenja stranice',
+    'pages_revision' => 'Revizija',
+    'pages_revisions' => 'Revizija stranice',
+    'pages_revisions_named' => 'Revizije stranice :pageName',
+    'pages_revision_named' => 'Revizija stranice :pageName',
+    'pages_revision_restored_from' => 'Oporavak iz #:id; :summary',
+    'pages_revisions_created_by' => 'Stvoreno od',
+    'pages_revisions_date' => 'Datum revizije',
+    'pages_revisions_number' => '#',
+    'pages_revisions_numbered' => 'Revizija #:id',
+    'pages_revisions_numbered_changes' => 'Revizija #:id Promjene',
+    'pages_revisions_changelog' => 'Dnevnik promjena',
+    'pages_revisions_changes' => 'Promjene',
+    'pages_revisions_current' => 'Trenutna verzija',
+    'pages_revisions_preview' => 'Pregled',
+    'pages_revisions_restore' => 'Vrati',
+    'pages_revisions_none' => 'Ova stranica nema revizija',
+    'pages_copy_link' => 'Kopiraj poveznicu',
+    'pages_edit_content_link' => 'Uredi sadržaj',
+    'pages_permissions_active' => 'Aktivna dopuštenja stranice',
+    'pages_initial_revision' => 'Početno objavljivanje',
+    'pages_initial_name' => 'Nova stranica',
+    'pages_editing_draft_notification' => 'Uređujete nacrt stranice posljednji put spremljen :timeDiff.',
+    'pages_draft_edited_notification' => 'Ova je stranica u međuvremenu ažurirana. Preporučujemo da odbacite ovaj nacrt.',
+    'pages_draft_edit_active' => [
+        'start_a' => ':count korisnika koji uređuju ovu stranicu',
+        'start_b' => ':userName je počeo uređivati ovu stranicu',
+        'time_a' => 'otkad je stranica posljednji put ažurirana',
+        'time_b' => 'u zadnjih :minCount minuta',
+        'message' => ':start :time. Pripazite na uzajamna ažuriranja!',
+    ],
+    'pages_draft_discarded' => 'Nacrt je odbijen jer je uređivač ažurirao postoječi sadržaj',
+    'pages_specific' => 'Predlošci stranice',
+    'pages_is_template' => 'Predložak stranice',
+
+    // Editor Sidebar
+    'page_tags' => 'Oznake stranice',
+    'chapter_tags' => 'Oznake poglavlja',
+    'book_tags' => 'Oznake knjiga',
+    'shelf_tags' => 'Oznake polica',
+    'tag' => 'Oznaka',
+    'tags' =>  'Tags',
+    'tag_name' =>  'Tag Name',
+    'tag_value' => 'Oznaka vrijednosti (neobavezno)',
+    'tags_explain' => "Add some tags to better categorise your content. \n You can assign a value to a tag for more in-depth organisation.",
+    'tags_add' => 'Dodaj oznaku',
+    'tags_remove' => 'Makni oznaku',
+    'attachments' => 'Prilozi',
+    'attachments_explain' => 'Dodajte datoteke ili poveznice za prikaz na vašoj stranici. Vidljive su na rubnoj oznaci stranice.',
+    'attachments_explain_instant_save' => 'Promjene se automatski spremaju.',
+    'attachments_items' => 'Dodane stavke',
+    'attachments_upload' => 'Dodaj datoteku',
+    'attachments_link' => 'Dodaj poveznicu',
+    'attachments_set_link' => 'Postavi poveznicu',
+    'attachments_delete' => 'Jeste li sigurni da želite izbrisati ovu stavku?',
+    'attachments_dropzone' => 'Dodajte datoteke ili kliknite ovdje',
+    'attachments_no_files' => 'Nijedna datoteka nije prenesena',
+    'attachments_explain_link' => 'Možete dodati poveznicu ako ne želite prenijeti datoteku. Poveznica može voditi na drugu stranicu ili datoteku.',
+    'attachments_link_name' => 'Ime poveznice',
+    'attachment_link' => 'Poveznica na privitak',
+    'attachments_link_url' => 'Poveznica na datoteku',
+    'attachments_link_url_hint' => 'Url ili stranica ili datoteka',
+    'attach' => 'Dodaj',
+    'attachments_insert_link' => 'Dodaj poveznicu na stranicu',
+    'attachments_edit_file' => 'Uredi datoteku',
+    'attachments_edit_file_name' => 'Ime datoteke',
+    'attachments_edit_drop_upload' => 'Dodaj datoteku ili klikni ovdje za prijenos',
+    'attachments_order_updated' => 'Ažurirani popis priloga',
+    'attachments_updated_success' => 'Ažurirani detalji priloga',
+    'attachments_deleted' => 'Izbrisani prilozi',
+    'attachments_file_uploaded' => 'Datoteka je uspješno prenešena',
+    'attachments_file_updated' => 'Datoteka je uspješno ažurirana',
+    'attachments_link_attached' => 'Poveznica je dodana na stranicu',
+    'templates' => 'Predlošci',
+    'templates_set_as_template' => 'Stranica je predložak',
+    'templates_explain_set_as_template' => 'Ovu stranicu možete postaviti pomoću predloška koji možete koristiti tijekom stvaranja drugih stranica. Ostali korisnici će ga također moći koristiti ako imaju dopuštenje.',
+    'templates_replace_content' => 'Zamjeni sadržaj stranice',
+    'templates_append_content' => 'Dodaj sadržaju stranice',
+    'templates_prepend_content' => 'Dodaj na sadržaj stranice',
+
+    // Profile View
+    'profile_user_for_x' => 'Korisnik za :time',
+    'profile_created_content' => 'Stvoreni sadržaj',
+    'profile_not_created_pages' => ':userName nije kreirao nijednu stranicu',
+    'profile_not_created_chapters' => ':userName nije kreirao nijedno poglavlje',
+    'profile_not_created_books' => ':userName nije kreirao nijednu knjigu',
+    'profile_not_created_shelves' => ':userName nije kreirao nijednu policu',
+
+    // Comments
+    'comment' => 'Komentar',
+    'comments' => 'Komentari',
+    'comment_add' => 'Dodaj komentar',
+    'comment_placeholder' => 'Komentar ostavi ovdje',
+    'comment_count' => '{0} Nema komentara|{1} 1 Komentar|[2,*] :count Komentara',
+    'comment_save' => 'Spremi komentar',
+    'comment_saving' => 'Spremanje komentara',
+    'comment_deleting' => 'Brisanje komentara',
+    'comment_new' => 'Novi komentar',
+    'comment_created' => 'komentirano :createDiff',
+    'comment_updated' => 'Ažurirano :updateDiff od :username',
+    'comment_deleted_success' => 'Izbrisani komentar',
+    'comment_created_success' => 'Dodani komentar',
+    'comment_updated_success' => 'Ažurirani komentar',
+    'comment_delete_confirm' => 'Jeste li sigurni da želite izbrisati ovaj komentar?',
+    'comment_in_reply_to' => 'Odgovor na :commentId',
+
+    // Revision
+    'revision_delete_confirm' => 'Jeste li sigurni da želite izbrisati ovaj ispravak?',
+    'revision_restore_confirm' => 'Jeste li sigurni da želite vratiti ovaj ispravak? Trenutni sadržaj će biti zamijenjen.',
+    'revision_delete_success' => 'Izbrisani ispravak',
+    'revision_cannot_delete_latest' => 'Posljednji ispravak se ne može izbrisati.'
+];
diff --git a/resources/lang/hr/errors.php b/resources/lang/hr/errors.php
new file mode 100644 (file)
index 0000000..4562108
--- /dev/null
@@ -0,0 +1,105 @@
+<?php
+/**
+ * Text shown in error messaging.
+ */
+return [
+
+    // Permissions
+    'permission' => 'Nemate dopuštenje za pristup traženoj stranici.',
+    'permissionJson' => 'Nemate potrebno dopuštenje.',
+
+    // Auth
+    'error_user_exists_different_creds' => 'Korisnik s mailom :email već postoji, ali s drugom vjerodajnicom.',
+    'email_already_confirmed' => 'Email je već potvrđen, pokušajte se logirati.',
+    'email_confirmation_invalid' => 'Ova vjerodajnica nije valjana ili je već bila korištena. Pokušajte se ponovno registrirati.',
+    'email_confirmation_expired' => 'Ova vjerodajnica je istekla. Poslan je novi email za pristup.',
+    'email_confirmation_awaiting' => 'Email adresa za račun koji se koristi mora biti potvrđen',
+    'ldap_fail_anonymous' => 'LDAP pristup nije uspio zbog anonimnosti',
+    'ldap_fail_authed' => 'LDAP pristup nije uspio',
+    'ldap_extension_not_installed' => 'LDAP PHP ekstenzija nije instalirana',
+    'ldap_cannot_connect' => 'Nemoguće pristupiti ldap serveru, problem s mrežom',
+    'saml_already_logged_in' => 'Već ste prijavljeni',
+    'saml_user_not_registered' => 'Korisnik :name nije registriran i automatska registracija je onemogućena',
+    'saml_no_email_address' => 'Nismo pronašli email adresu za ovog korisnika u vanjskim sustavima',
+    'saml_invalid_response_id' => 'Sustav za autentifikaciju nije prepoznat. Ovaj problem možda je nastao zbog vraćanja nakon prijave.',
+    'saml_fail_authed' => 'Prijava pomoću :system nije uspjela zbog neuspješne autorizacije',
+    'social_no_action_defined' => 'Nije definirana nijedna radnja',
+    'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
+    'social_account_in_use' => 'Ovaj :socialAccount račun se već koristi. Pokušajte se prijaviti pomoću :socialAccount računa.',
+    'social_account_email_in_use' => 'Ovaj mail :email se već koristi. Ako već imate naš račun možete se prijaviti pomoću :socialAccount računa u postavkama vašeg profila.',
+    'social_account_existing' => 'Ovaj :socialAccount je već dodan u vaš profil.',
+    'social_account_already_used_existing' => 'Ovaj :socialAccount već koristi drugi korisnik.',
+    'social_account_not_used' => 'Ovaj :socialAccount račun ne koristi nijedan korisnik. Dodajte ga u postavke svog profila.',
+    'social_account_register_instructions' => 'Ako nemate račun možete se registrirati pomoću :socialAccount opcija.',
+    'social_driver_not_found' => 'Nije pronađeno',
+    'social_driver_not_configured' => 'Postavke vašeg :socialAccount računa nisu ispravno postavljene.',
+    'invite_token_expired' => 'Vaša pozivnica je istekla. Pokušajte ponovno postaviti lozinku.',
+
+    // System
+    'path_not_writable' => 'Datoteka :filePath ne može se prenijeti. Učinite je lakše prepoznatljivom vašem serveru.',
+    'cannot_get_image_from_url' => 'Nemoguće preuzeti sliku sa :url',
+    'cannot_create_thumbs' => 'Provjerite imate li instaliranu GD PHP ekstenziju.',
+    'server_upload_limit' => 'Prevelika količina za server. Pokušajte prenijeti manju veličinu.',
+    'uploaded'  => 'Prevelika količina za server. Pokušajte prenijeti manju veličinu.',
+    'image_upload_error' => 'Problem s prenosom slike',
+    'image_upload_type_error' => 'Nepodržani format slike',
+    'file_upload_timeout' => 'Isteklo vrijeme za prijenos datoteke.',
+
+    // Attachments
+    'attachment_not_found' => 'Prilozi nisu pronađeni',
+
+    // Pages
+    'page_draft_autosave_fail' => 'Problem sa spremanjem nacrta. Osigurajte stabilnu internetsku vezu.',
+    'page_custom_home_deletion' => 'Stranica označena kao naslovnica ne može se izbrisati',
+
+    // Entities
+    'entity_not_found' => 'Nije pronađeno',
+    'bookshelf_not_found' => 'Polica nije pronađena',
+    'book_not_found' => 'Knjiga nije pronađena',
+    'page_not_found' => 'Stranica nije pronađena',
+    'chapter_not_found' => 'Poglavlje nije pronađeno',
+    'selected_book_not_found' => 'Odabrana knjiga nije pronađena',
+    'selected_book_chapter_not_found' => 'Odabrane knjige ili poglavlja nisu pronađena',
+    'guests_cannot_save_drafts' => 'Gosti ne mogu spremiti nacrte',
+
+    // Users
+    'users_cannot_delete_only_admin' => 'Ne možete izbrisati',
+    'users_cannot_delete_guest' => 'Ne možete izbrisati',
+
+    // Roles
+    'role_cannot_be_edited' => 'Ne može se urediti',
+    'role_system_cannot_be_deleted' => 'Sistemske postavke ne možete izbrisati',
+    'role_registration_default_cannot_delete' => 'Ne može se izbrisati',
+    'role_cannot_remove_only_admin' => 'Učinite drugog korisnika administratorom prije uklanjanja ove administratorske uloge.',
+
+    // Comments
+    'comment_list' => 'Pogreška prilikom dohvaćanja komentara.',
+    'cannot_add_comment_to_draft' => 'Ne možete ostaviti komentar na ovaj nacrt.',
+    'comment_add' => 'Greška prilikom dodavanja ili ažuriranja komentara.',
+    'comment_delete' => 'Greška prilikom brisanja komentara.',
+    'empty_comment' => 'Ne možete ostaviti prazan komentar.',
+
+    // Error pages
+    '404_page_not_found' => 'Stranica nije pronađena',
+    'sorry_page_not_found' => 'Žao nam je, stranica koju tražite nije pronađena.',
+    'sorry_page_not_found_permission_warning' => 'Ako smatrate da ova stranica još postoji, ali je ne vidite, moguće je da nemate omogućen pristup.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
+    'return_home' => 'Povratak na početno',
+    'error_occurred' => 'Došlo je do pogreške',
+    'app_down' => ':appName trenutno nije dostupna',
+    'back_soon' => 'Uskoro će se vratiti.',
+
+    // API errors
+    'api_no_authorization_found' => 'Nije pronađena autorizacija',
+    'api_bad_authorization_format' => 'Pogreška prilikom autorizacije',
+    'api_user_token_not_found' => 'Format autorizacije nije podržan',
+    'api_incorrect_token_secret' => 'Netočan API token',
+    'api_user_no_api_permission' => 'Vlasnik API tokena nema potrebna dopuštenja',
+    'api_user_token_expired' => 'Autorizacija je istekla',
+
+    // Settings & Maintenance
+    'maintenance_test_email_failure' => 'Pogreška prilikom slanja testnog email:',
+
+];
diff --git a/resources/lang/hr/pagination.php b/resources/lang/hr/pagination.php
new file mode 100644 (file)
index 0000000..1165a10
--- /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; Prethodno',
+    'next'     => 'Sljedeće &raquo;',
+
+];
diff --git a/resources/lang/hr/passwords.php b/resources/lang/hr/passwords.php
new file mode 100644 (file)
index 0000000..51104ab
--- /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' => 'Lozinka mora imati najmanje 8 znakova i biti potvrđena.',
+    'user' => "We can't find a user with that e-mail address.",
+    'token' => 'Ponovno postavljanje lozinke nemoguće putem ove adrese.',
+    'sent' => 'Na vašu email adresu poslana je poveznica za ponovno postavljanje!',
+    'reset' => 'Vaša je lozinka ponovno postavljena!',
+
+];
diff --git a/resources/lang/hr/settings.php b/resources/lang/hr/settings.php
new file mode 100644 (file)
index 0000000..9c4d6eb
--- /dev/null
@@ -0,0 +1,267 @@
+<?php
+/**
+ * Settings text strings
+ * Contains all text strings used in the general settings sections of BookStack
+ * including users and roles.
+ */
+return [
+
+    // Common Messages
+    'settings' => 'Postavke',
+    'settings_save' => 'Spremi postavke',
+    'settings_save_success' => 'Postavke spremljene',
+
+    // App Settings
+    'app_customization' => 'Prilagođavanje',
+    'app_features_security' => 'Značajke & Sigurnost',
+    'app_name' => 'Ime aplikacije',
+    'app_name_desc' => 'Ime je vidljivo u zaglavlju i svakoj sistemskoj poruci.',
+    'app_name_header' => 'Prikaži ime u zaglavlju',
+    'app_public_access' => 'Javni pristup',
+    'app_public_access_desc' => 'Omogućavanje ove postavke pristup sadržaju imat će svi posjetitelji BookStack čak i ako nisu prijavljeni.',
+    'app_public_access_desc_guest' => 'Javni pristup može se urediti putem opcije "Gost".',
+    'app_public_access_toggle' => 'Dozvoli javni pristup',
+    'app_public_viewing' => 'Dozvoljen javni pristup?',
+    'app_secure_images' => 'Visoka razina sigurnosti prijenosa slika',
+    'app_secure_images_toggle' => 'Omogući visoku sigurnost prijenosa slika',
+    'app_secure_images_desc' => 'Zbog specifične izvedbe sve su slike javne. Osigurajte da indeksi direktorija nisu omogućeni kako bi se spriječio neovlašten pristup.',
+    'app_editor' => 'Uređivač stranice',
+    'app_editor_desc' => 'Odaberite uređivače stranica',
+    'app_custom_html' => 'Prilagođeni HTML sadržaj',
+    'app_custom_html_desc' => 'Sav sadržaj dodan ovdje bit će umetnut na dno <glavne> stranice. To je korisno za stiliziranje i dodavanje analitičkog koda.',
+    'app_custom_html_disabled_notice' => 'Prilagođeni HTML je onemogućen kako bi se osiguralo vraćanje promjena u slučaju kvara.',
+    'app_logo' => 'Logo aplikacije',
+    'app_logo_desc' => 'Slika smije biti najviše 43px u visinu. <br>Velike slike će biti smanjene.',
+    'app_primary_color' => 'Primarna boja aplikacije',
+    'app_primary_color_desc' => 'Postavlja primarnu boju za aplikaciju uključujući natpis, gumbe i veze.',
+    'app_homepage' => 'Glavna stranica aplikacije',
+    'app_homepage_desc' => 'Odaberite prikaz svoje glavne stranice umjesto već zadane. Za odabrane stranice ne vrijede zadana dopuštenja.',
+    'app_homepage_select' => 'Odaberi stranicu',
+    'app_footer_links' => 'Podnožje',
+    'app_footer_links_desc' => 'Odaberite poveznice koje će biti vidljive u podnožju većina stranica čak i na nekima koje ne zahtijevaju prijavu. Na primjer, oznaku "trans::common.privacy_policy" možete koristiti za sistemski definirani prijevod teksta "Politika Privatnosti", a za "Uvjete korištenja" možete koristiti "trans::common.terms_of_service".',
+    'app_footer_links_label' => 'Oznaka veze',
+    'app_footer_links_url' => 'Oznaka URL',
+    'app_footer_links_add' => 'Dodaj vezu na podnožje',
+    'app_disable_comments' => 'Onemogući komentare',
+    'app_disable_comments_toggle' => 'Onemogući komentare',
+    'app_disable_comments_desc' => 'Onemogući komentare za sve stranice u aplikaciji. <br> Postojeći komentari nisu prikazani.',
+
+    // Color settings
+    'content_colors' => 'Boja sadržaja',
+    'content_colors_desc' => 'Postavljanje boja za sve elemente stranice. Preporuča se odabir boja čija je svjetlina slična zadanim bojama.',
+    'bookshelf_color' => 'Boja police',
+    'book_color' => 'Boja knjige',
+    'chapter_color' => 'Boja poglavlja',
+    'page_color' => 'Boja stranice',
+    'page_draft_color' => 'Boja nacrta',
+
+    // Registration Settings
+    'reg_settings' => 'Registracija',
+    'reg_enable' => 'Omogući registraciju',
+    'reg_enable_toggle' => 'Omogući registraciju',
+    'reg_enable_desc' => 'Ako je omogućeno korisnik se može sam registrirati nakon čega će mu biti dodijeljena jedna od korisničkih uloga.',
+    'reg_default_role' => 'Zadaj ulogu korisnika nakon registracije',
+    'reg_enable_external_warning' => 'Gornja opcija se zanemaruje ako postoji LDAP ili SAML autorifikacija. Korisnički računi za nepostojeće članove automatski će se kreirati ako je vanjska provjera autentičnosti bila uspješna.',
+    'reg_email_confirmation' => 'Potvrda e maila',
+    'reg_email_confirmation_toggle' => 'Zahtjev za potvrdom e maila',
+    'reg_confirm_email_desc' => 'Ako postoje ograničenja domene potvrda e maila će se zahtijevati i ova će se opcija zanemariti.',
+    'reg_confirm_restrict_domain' => 'Ograničenja domene',
+    'reg_confirm_restrict_domain_desc' => 'Unesite popis email domena kojima želite ograničiti registraciju i odvojite ih zarezom. Korisnicima će se slati email prije interakcije s aplikacijom. <br> Uzmite u obzir da će korisnici moći koristiti druge e mail adrese nakon uspješne registracije.',
+    'reg_confirm_restrict_domain_placeholder' => 'Bez ograničenja',
+
+    // Maintenance settings
+    'maint' => 'Održavanje',
+    'maint_image_cleanup' => 'Čišćenje slika',
+    '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_delete_images_only_in_revisions' => 'Izbriši slike koje postoje u prijašnjim revizijama',
+    'maint_image_cleanup_run' => 'Pokreni čišćenje',
+    'maint_image_cleanup_warning' => ':count moguće neiskorištene slike. Jeste li sigurni da želite izbrisati ove slike?',
+    'maint_image_cleanup_success' => ':count moguće neiskorištene slike su pronađene i izbrisane!',
+    'maint_image_cleanup_nothing_found' => 'Nema neiskorištenih slika, Ništa nije izbrisano!',
+    'maint_send_test_email' => 'Pošalji testni Email',
+    'maint_send_test_email_desc' => 'Na ovaj način šaljete testni Email na adresu navedenu u vašem profilu.',
+    'maint_send_test_email_run' => 'Pošalji testni email',
+    'maint_send_test_email_success' => 'Email je poslan na :address',
+    'maint_send_test_email_mail_subject' => 'Testni email',
+    'maint_send_test_email_mail_greeting' => 'Email se može koristiti!',
+    'maint_send_test_email_mail_text' => 'Čestitamo! Ako ste primili ovaj e mail znači da ćete ga moći koristiti.',
+    'maint_recycle_bin_desc' => 'Izbrisane police, knjige, poglavlja i stranice poslane su u Recycle bin i mogu biti vraćene ili trajno izbrisane. Starije stavke bit će automatski izbrisane nakon nekog vremena što ovisi o konfiguraciji sustava.',
+    'maint_recycle_bin_open' => 'Otvori Recycle Bin',
+
+    // Recycle Bin
+    'recycle_bin' => 'Recycle Bin',
+    'recycle_bin_desc' => 'Ovdje možete vratiti izbrisane stavke ili ih trajno ukloniti iz sustava. Popis nije filtriran kao što su to popisi u kojima su omogućeni filteri.',
+    'recycle_bin_deleted_item' => 'Izbrisane stavke',
+    'recycle_bin_deleted_by' => 'Izbrisano od',
+    'recycle_bin_deleted_at' => 'Vrijeme brisanja',
+    'recycle_bin_permanently_delete' => 'Trajno izbrisano',
+    'recycle_bin_restore' => 'Vrati',
+    'recycle_bin_contents_empty' => 'Recycle Bin je prazan',
+    'recycle_bin_empty' => 'Isprazni Recycle Bin',
+    'recycle_bin_empty_confirm' => 'Ovo će trajno obrisati sve stavke u Recycle Bin i sadržaje povezane s njima. Jeste li sigurni da želite isprazniti Recycle Bin?',
+    'recycle_bin_destroy_confirm' => 'Ovom radnjom ćete trajno izbrisati ovu stavku i nećete je više moći vratiti. Želite li je trajno izbrisati?',
+    'recycle_bin_destroy_list' => 'Stavke koje treba izbrisati',
+    'recycle_bin_restore_list' => 'Stavke koje treba vratiti',
+    'recycle_bin_restore_confirm' => 'Ova radnja vraća izbrisane stavke i njene podređene elemente na prvobitnu lokaciju. Ako je nadređena stavka izbrisana i nju treba vratiti.',
+    'recycle_bin_restore_deleted_parent' => 'S obzirom da je nadređena stavka obrisana najprije treba vratiti nju.',
+    'recycle_bin_destroy_notification' => 'Ukupno izbrisane :count stavke iz Recycle Bin',
+    'recycle_bin_restore_notification' => 'Ukupno vraćene :count stavke iz Recycle Bin',
+
+    // Audit Log
+    'audit' => 'Dnevnik revizije',
+    'audit_desc' => 'Ovaj dnevnik revizije prikazuje popis aktivnosti zabilježene u sustavu. Ovaj popis nije definiran budući da nisu postavljeni filteri.',
+    'audit_event_filter' => 'Filter događaja',
+    'audit_event_filter_no_filter' => 'Bez filtera',
+    'audit_deleted_item' => 'Izbrisane stavke',
+    'audit_deleted_item_name' => 'Ime: :name',
+    'audit_table_user' => 'Korisnik',
+    'audit_table_event' => 'Događaj',
+    'audit_table_related' => 'Povezana stavka ili detalj',
+    'audit_table_date' => 'Datum aktivnosti',
+    'audit_date_from' => 'Rangiraj datum od',
+    'audit_date_to' => 'Rangiraj datum do',
+
+    // Role Settings
+    'roles' => 'Uloge',
+    'role_user_roles' => 'Uloge korisnika',
+    'role_create' => 'Stvori novu ulogu',
+    'role_create_success' => 'Uloga uspješno stvorena',
+    'role_delete' => 'Izbriši ulogu',
+    'role_delete_confirm' => 'Ovo će izbrisati ulogu povezanu s imenom \':roleName\'.',
+    'role_delete_users_assigned' => 'Ova uloga dodijeljena je :userCount. Ako želite premjestiti korisnike iz ove uloge odaberite novu ulogu u nastavku.',
+    'role_delete_no_migration' => "Don't migrate users",
+    'role_delete_sure' => 'Jeste li sigurni da želite obrisati ovu ulogu?',
+    'role_delete_success' => 'Uloga je uspješno izbrisana',
+    'role_edit' => 'Uredi ulogu',
+    'role_details' => 'Detalji uloge',
+    'role_name' => 'Ime uloge',
+    'role_desc' => 'Kratki opis uloge',
+    'role_external_auth_id' => 'Autorizacija',
+    'role_system' => 'Dopuštenja sustava',
+    'role_manage_users' => 'Upravljanje korisnicima',
+    'role_manage_roles' => 'Upravljanje ulogama i dopuštenjima',
+    'role_manage_entity_permissions' => 'Upravljanje dopuštenjima nad knjigama, poglavljima i stranicama',
+    'role_manage_own_entity_permissions' => 'Upravljanje dopuštenjima vlastitih knjiga, poglavlja i stranica',
+    'role_manage_page_templates' => 'Upravljanje predlošcima stranica',
+    'role_access_api' => 'API pristup',
+    'role_manage_settings' => 'Upravljanje postavkama aplikacija',
+    'role_asset' => 'Upravljanje vlasništvom',
+    'roles_system_warning' => 'Uzmite u obzir da pristup bilo kojem od ovih dopuštenja dozvoljavate korisniku upravljanje dopuštenjima ostalih u sustavu. Ova dopuštenja dodijelite pouzdanim korisnicima.',
+    'role_asset_desc' => 'Ova dopuštenja kontroliraju zadane pristupe. Dopuštenja za knjige, poglavlja i stranice ih poništavaju.',
+    'role_asset_admins' => 'Administratori automatski imaju pristup svim sadržajima, ali ove opcije mogu prikazati ili sakriti korisnička sučelja.',
+    'role_all' => 'Sve',
+    'role_own' => 'Vlastito',
+    'role_controlled_by_asset' => 'Kontrolirano od strane vlasnika',
+    'role_save' => 'Spremi ulogu',
+    'role_update_success' => 'Uloga uspješno ažurirana',
+    'role_users' => 'Korisnici u ovoj ulozi',
+    'role_users_none' => 'Trenutno nijedan korisnik nije u ovoj ulozi',
+
+    // Users
+    'users' => 'Korisnici',
+    'user_profile' => 'Profil korisnika',
+    'users_add_new' => 'Dodajte novog korisnika',
+    'users_search' => 'Pretražite korisnike',
+    'users_latest_activity' => 'Zadnje aktivnosti',
+    'users_details' => 'Detalji korisnika',
+    'users_details_desc' => 'Postavite prikaz imena i email adrese za ovog korisnika. Email adresa koristit će se za prijavu u aplikaciju.',
+    'users_details_desc_no_email' => 'Postavite prikaz imena ovog korisnika da ga drugi mogu prepoznati.',
+    'users_role' => 'Uloge korisnika',
+    'users_role_desc' => 'Odaberite koje će uloge biti dodijeljene ovom korisniku. Ako korisnik ima više uloga njihova će se dopuštenja prilagoditi.',
+    'users_password' => 'Lozinka korisnika',
+    'users_password_desc' => 'Postavite lozinku za prijavu u aplikaciju. Mora imati najmanje 6 znakova.',
+    'users_send_invite_text' => 'Možete odabrati slanje e maila korisniku i dozvoliti mu da postavi svoju lozinku ili vi to možete učiniti za njega.',
+    'users_send_invite_option' => 'Pošaljite pozivnicu korisniku putem emaila',
+    'users_external_auth_id' => 'Vanjska autorizacija',
+    'users_external_auth_id_desc' => 'Ovaj ID koristi se za komunikaciju s vanjskim sustavom za autorizaciju.',
+    'users_password_warning' => 'Ispunite dolje samo ako želite promijeniti lozinku.',
+    'users_system_public' => 'Ovaj korisnik predstavlja bilo kojeg gosta. Dodjeljuje se automatski.',
+    'users_delete' => 'Izbrišite korisnika',
+    'users_delete_named' => 'Izbrišite korisnika :userName',
+    'users_delete_warning' => 'Ovo će ukloniti korisnika \':userName\' iz sustava.',
+    'users_delete_confirm' => 'Jeste li sigurni da želite izbrisati ovog korisnika?',
+    'users_migrate_ownership' => 'Premjestite vlasništvo',
+    'users_migrate_ownership_desc' => 'Ovdje odaberite korisnika kojem ćete dodijeliti vlasništvo i sve stavke povezane s njim.',
+    'users_none_selected' => 'Nije odabran nijedan korisnik',
+    'users_delete_success' => 'Korisnik je uspješno premješten',
+    'users_edit' => 'Uredite korisnika',
+    'users_edit_profile' => 'Uredite profil',
+    'users_edit_success' => 'Korisnik je uspješno ažuriran',
+    'users_avatar' => 'Korisnički avatar',
+    'users_avatar_desc' => 'Odaberite sliku koja će predstavljati korisnika. Maksimalno 256px.',
+    'users_preferred_language' => 'Prioritetni jezik',
+    'users_preferred_language_desc' => 'Ova će opcija promijeniti jezik korisničkog sučelja. Neće utjecati na sadržaj.',
+    'users_social_accounts' => 'Računi društvenih mreža',
+    'users_social_accounts_info' => 'Ovdje možete povezati račun s onim na društvenim mrežama za bržu i lakšu prijavu. Ako se odspojite ovdje to neće utjecati na prethodnu autorizaciju. Na postavkama računa vaše društvene mreže možete opozvati pristup.',
+    'users_social_connect' => 'Poveži račun',
+    'users_social_disconnect' => 'Odvoji račun',
+    'users_social_connected' => ':socialAccount račun je uspješno dodan vašem profilu.',
+    'users_social_disconnected' => ':socialAccount račun je uspješno odvojen od vašeg profila.',
+    'users_api_tokens' => 'API tokeni',
+    'users_api_tokens_none' => 'Nijedan API token nije stvoren za ovog korisnika',
+    'users_api_tokens_create' => 'Stvori token',
+    'users_api_tokens_expires' => 'Isteklo',
+    'users_api_tokens_docs' => 'API dokumentacija',
+
+    // API Tokens
+    'user_api_token_create' => 'Stvori API token',
+    'user_api_token_name' => 'Ime',
+    'user_api_token_name_desc' => 'Imenujte svoj token na način da prepoznate njegovu svrhu.',
+    'user_api_token_expiry' => 'Datum isteka',
+    'user_api_token_expiry_desc' => 'Postavite datum kada token istječe. Ostavljanjem ovog polja praznim automatski se postavlja dugoročno razdoblje.',
+    'user_api_token_create_secret_message' => 'Odmah nakon kreiranja tokena prikazat će se "Token ID" i "Token Secret". To će se prikazati samo jednom i zato preporučujemo da ga spremite na sigurno.',
+    'user_api_token_create_success' => 'API token uspješno kreiran',
+    'user_api_token_update_success' => 'API token uspješno ažuriran',
+    'user_api_token' => 'API token',
+    'user_api_token_id' => 'Token ID',
+    'user_api_token_id_desc' => 'Ovaj sistemski generiran identifikator ne može se uređivati i bit će potreban pri API zahtjevima.',
+    'user_api_token_secret' => 'Token Secret',
+    'user_api_token_secret_desc' => 'Ovaj sistemski generirani Token Secret trebat ćete za API zahtjev. Prikazuje se samo prvi put i zato ga spremite na sigurno.',
+    'user_api_token_created' => 'Token kreiran :timeAgo',
+    'user_api_token_updated' => 'Token ažuriran :timeAgo',
+    'user_api_token_delete' => 'Izbriši token',
+    'user_api_token_delete_warning' => 'Ovo će potpuno izbrisati API token naziva \':tokenName\' iz našeg sustava.',
+    'user_api_token_delete_confirm' => 'Jeste li sigurni da želite izbrisati ovaj API token?',
+    'user_api_token_delete_success' => 'API token uspješno izbrisan',
+
+    //! 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',
+        'bs' => 'Bosanski',
+        'ca' => 'Català',
+        'cs' => 'Česky',
+        'da' => 'Dansk',
+        'de' => 'Deutsch (Sie)',
+        'de_informal' => 'Deutsch (Du)',
+        'es' => 'Español',
+        'es_AR' => 'Español Argentina',
+        'fr' => 'Français',
+        'he' => 'עברית',
+        'hr' => 'Hrvatski',
+        'hu' => 'Magyar',
+        'id' => 'Bahasa Indonesia',
+        'it' => 'Italian',
+        'ja' => '日本語',
+        'ko' => '한국어',
+        'lv' => 'Latviešu Valoda',
+        'nl' => 'Nederlands',
+        'nb' => 'Norsk (Bokmål)',
+        'pl' => 'Polski',
+        'pt' => 'Português',
+        '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/hr/validation.php b/resources/lang/hr/validation.php
new file mode 100644 (file)
index 0000000..5b1849c
--- /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'             => ':attribute mora biti prihvaćen.',
+    'active_url'           => ':attribute nema valjan URL.',
+    'after'                => ':attribute mora biti nakon :date.',
+    'alpha'                => ':attribute može sadržavati samo slova.',
+    'alpha_dash'           => ':attribute  može sadržavati samo slova, brojeve, crtice i donje crtice.',
+    'alpha_num'            => ':attribute može sadržavati samo slova i brojeve.',
+    'array'                => ':attribute mora biti niz.',
+    'before'               => ':attribute mora biti prije :date.',
+    'between'              => [
+        'numeric' => ':attribute mora biti između :min i :max.',
+        'file'    => ':attribute mora biti između :min i :max kilobajta.',
+        'string'  => ':attribute mora biti između :min i :max znakova.',
+        'array'   => ':attribute mora biti između :min i :max stavki',
+    ],
+    'boolean'              => ':attribute mora biti točno ili netočno.',
+    'confirmed'            => ':attribute potvrde se ne podudaraju.',
+    'date'                 => ':attribute nema valjani datum.',
+    'date_format'          => ':attribute ne odgovara formatu :format.',
+    'different'            => ':attribute i :other se moraju razlikovati.',
+    'digits'               => ':attribute mora biti :digits znakova.',
+    'digits_between'       => ':attribute mora biti između :min i :max znamenki.',
+    'email'                => ':attribute mora biti valjana email adresa.',
+    'ends_with' => ':attribute mora završiti s :values',
+    'filled'               => ':attribute polje je obavezno.',
+    'gt'                   => [
+        'numeric' => ':attribute mora biti veći od :value.',
+        'file'    => ':attribute mora biti veći od :value  kilobajta.',
+        'string'  => ':attribute mora biti veći od :value znakova',
+        'array'   => ':attribute mora biti veći od :value stavki.',
+    ],
+    'gte'                  => [
+        'numeric' => ':attribute mora biti veći ili jednak :value.',
+        'file'    => ':attribute mora biti veći ili jednak :value kilobajta.',
+        'string'  => ':attribute mora biti veći ili jednak :value znakova.',
+        'array'   => ':attribute mora imati :value stavki ili više.',
+    ],
+    'exists'               => 'Odabrani :attribute ne vrijedi.',
+    'image'                => ':attribute mora biti slika.',
+    'image_extension'      => ':attribute mora imati valjanu i podržanu ekstenziju.',
+    'in'                   => 'Odabrani :attribute ne vrijedi.',
+    'integer'              => ':attribute mora biti cijeli broj.',
+    'ip'                   => ':attribute mora biti valjana IP adresa.',
+    'ipv4'                 => ':attribute mora biti valjana IPv4 adresa.',
+    'ipv6'                 => ':attribute mora biti valjana IPv6 adresa.',
+    'json'                 => ':attribute mora biti valjani JSON niz.',
+    'lt'                   => [
+        'numeric' => ':attribute mora biti manji od :value.',
+        'file'    => ':attribute mora biti manji od :value kilobajta.',
+        'string'  => ':attribute mora biti manji od :value znakova.',
+        'array'   => ':attribute mora biti manji od :value stavki.',
+    ],
+    'lte'                  => [
+        'numeric' => ':attribute mora biti manji ili jednak :value.',
+        'file'    => ':attribute mora biti manji ili jednak :value kilobajta.',
+        'string'  => ':attribute mora biti manji ili jednak :value znakova.',
+        'array'   => ':attribute mora imati više od :value stavki.',
+    ],
+    'max'                  => [
+        'numeric' => ':attribute ne smije biti veći od :max.',
+        'file'    => ':attribute ne smije biti veći od :max kilobajta.',
+        'string'  => ':attribute ne smije biti duži od :max znakova.',
+        'array'   => ':attribute ne smije imati više od :max stavki.',
+    ],
+    'mimes'                => ':attribute mora biti datoteka tipa: :values.',
+    'min'                  => [
+        'numeric' => ':attribute mora biti najmanje :min.',
+        'file'    => ':attribute mora imati najmanje :min kilobajta.',
+        'string'  => ':attribute mora imati najmanje :min znakova.',
+        'array'   => ':attribute mora imati najmanje :min stavki.',
+    ],
+    'not_in'               => 'Odabrani :attribute ne vrijedi.',
+    'not_regex'            => 'Format :attribute nije valjan.',
+    'numeric'              => ':attribute mora biti broj.',
+    'regex'                => 'Format :attribute nije valjan.',
+    'required'             => ':attribute polje je obavezno.',
+    'required_if'          => 'Polje :attribute je obavezno kada :other je :value.',
+    'required_with'        => 'Polje :attribute je potrebno kada :values je sadašnjost.',
+    'required_with_all'    => 'Polje :attribute je potrebno kada :values je sadašnjost.',
+    'required_without'     => 'Polje :attribute je potrebno kada :values nije sadašnjost.',
+    'required_without_all' => 'Polje :attribute je potrebno kada ništa od :values nije sadašnjost.',
+    'same'                 => ':attribute i :other se moraju podudarati.',
+    'safe_url'             => 'Navedena veza možda nije sigurna.',
+    'size'                 => [
+        'numeric' => ':attribute mora biti :size.',
+        'file'    => ':attribute mora biti :size kilobajta.',
+        'string'  => ':attribute mora biti :size znakova.',
+        'array'   => ':attribute mora sadržavati :size stavki.',
+    ],
+    'string'               => ':attribute mora biti niz.',
+    'timezone'             => ':attribute mora biti valjan.',
+    'unique'               => ':attribute se već koristi.',
+    'url'                  => 'Format :attribute nije valjan.',
+    'uploaded'             => 'Datoteka se ne može prenijeti. Server možda ne prihvaća datoteke te veličine.',
+
+    // Custom validation lines
+    'custom' => [
+        'password-confirm' => [
+            'required_with' => 'Potrebna potvrda lozinke',
+        ],
+    ],
+
+    // Custom validation attributes
+    'attributes' => [],
+];
index 8d22605e36c1c0549ebc99f83866163d44296c7b..ff2ddb4908041f68b3ae3c6b4eae53edc3280fdc 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'törölte a könyvespolcot:',
     'bookshelf_delete_notification'    => 'Könyvespolc sikeresen törölve',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'megjegyzést fűzött hozzá:',
     'permissions_update'          => 'updated permissions',
index f543a73b160cc1a4c07e682a37a0808a1c6ab59e..948bbaefd1a75ac9f4ffc5bf82faa600f0f13dae 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Eltávolítás',
     'add' => 'Hozzáadás',
     'fullscreen' => 'Teljes képernyő',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Rendezési beállítások',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Morzsa',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profil menü',
     'view_profile' => 'Profil megtekintése',
     'edit_profile' => 'Profil szerkesztése',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Információ',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Tartalom',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Probléma esetén a lenti ":actionText" gombra kell kattintani, majd ki kell másolni a lenti webcímet és be kell illeszteni egy böngészőbe:',
index 293e4ebc455c8713a4ac49d272e2b10a3f2e4179..4d789bfd082bfa32b5ce7f1bc0f8da4959ef8d59 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Képek',
     'my_recent_drafts' => 'Legutóbbi vázlataim',
     'my_recently_viewed' => 'Általam legutóbb megtekintett',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Még nincsenek általam megtekintett oldalak',
     'no_pages_recently_created' => 'Nincsenek legutóbb létrehozott oldalak',
     'no_pages_recently_updated' => 'Nincsenek legutóbb frissített oldalak',
index 64229a19f3d3283025093b3c2a159890c82bcb21..a16bef5290a00c3882421bf12020282165d82319 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Oldal nem található',
     'sorry_page_not_found' => 'Sajnáljuk, a keresett oldal nem található.',
     'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Vissza a kezdőlapra',
     'error_occurred' => 'Hiba örtént',
     'app_down' => ':appName jelenleg nem üzemel',
index ba748aafedd5a51da97fcb60cf6d070333e3f430..d8f05200852f285585ac4a8ebabbc4490117b069 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 2269cb2252d5192fd53641f2ca071694868e7291..1255e32dcf9c336687d24c8e0dca1a1c2a369ab4 100644 (file)
@@ -6,10 +6,10 @@
 return [
 
     // Pages
-    'page_create'                 => 'halaman dibuat',
+    'page_create'                 => 'telah membuat halaman',
     'page_create_notification'    => 'Halaman Berhasil dibuat',
     'page_update'                 => 'halaman diperbaharui',
-    'page_update_notification'    => 'Berhasil mengupdate halaman',
+    'page_update_notification'    => 'Halaman Berhasil Diperbarui',
     'page_delete'                 => 'halaman dihapus',
     'page_delete_notification'    => 'Berhasil menghapus halaman',
     'page_restore'                => 'halaman telah dipulihkan',
@@ -20,7 +20,7 @@ return [
     'chapter_create'              => 'membuat bab',
     'chapter_create_notification' => 'Bab berhasil dibuat',
     'chapter_update'              => 'bab diperbaharui',
-    'chapter_update_notification' => 'Bab berhasil diupdate',
+    'chapter_update_notification' => 'Bab Berhasil Dipebarui',
     'chapter_delete'              => 'hapus bab',
     'chapter_delete_notification' => 'Bab berhasil dihapus',
     'chapter_move'                => 'bab dipindahkan',
@@ -29,21 +29,25 @@ return [
     'book_create'                 => 'membuat buku',
     'book_create_notification'    => 'Buku berhasil dibuat',
     'book_update'                 => 'update buku',
-    'book_update_notification'    => 'Buku berhasil diupdate',
+    'book_update_notification'    => 'Buku Berhasil Diperbarui',
     'book_delete'                 => 'hapus buku',
     'book_delete_notification'    => 'Buku berhasil dihapus',
-    'book_sort'                   => 'urutkan buku',
+    'book_sort'                   => 'buku yang diurutkan',
     'book_sort_notification'      => 'Buku berhasil diurutkan',
 
     // Bookshelves
     'bookshelf_create'            => 'membuat rak',
     'bookshelf_create_notification'    => 'Rak berhasil dibuat',
     'bookshelf_update'                 => 'update rak',
-    'bookshelf_update_notification'    => 'Rak berhasil diupdate',
+    'bookshelf_update_notification'    => 'Rak Berhasil Diperbarui',
     'bookshelf_delete'                 => 'hapus rak buku',
     'bookshelf_delete_notification'    => 'Rak berhasil dihapus',
 
+    // Favourites
+    'favourite_add_notification' => '":name" telah ditambahkan ke favorit Anda',
+    'favourite_remove_notification' => '":name" telah dihapus dari favorit Anda',
+
     // Other
     'commented_on'                => 'berkomentar pada',
-    'permissions_update'          => 'perbaharui izin',
+    'permissions_update'          => 'izin diperbarui',
 ];
index e29410138f20e1dc640622e352b495d20480a564..893ff76032b998cb56b62edb7fe6b3a68156f8dd 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Hapus',
     'add' => 'Tambah',
     'fullscreen' => 'Layar Penuh',
+    'favourite' => 'Favorit',
+    'unfavourite' => 'Tidak favorit',
+    'next' => 'Lanjut',
+    'previous' => 'Sebelumnya',
 
     // Sort Options
     'sort_options' => 'Sortir Pilihan',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Perluas Menu Tajuk',
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'Tampilkan profil',
     'edit_profile' => 'Sunting Profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informasi',
+    'tab_info_label' => 'Tab Menampilkan Informasi Sekunder',
     'tab_content' => 'Konten',
+    'tab_content_label' => 'Tab Menampilkan Informasi Utama',
 
     // Email Content
     'email_action_help' => 'Jika Anda mengalami masalah saat mengklik tombol ":actionText", salin dan tempel URL di bawah ini ke browser web Anda:',
index 7764c1a3eb0e0052c081b071a3404c738d701200..5f6afb807a34dc4c4ef39bf27b9d3ae8a33c4c57 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Gambar-gambar',
     'my_recent_drafts' => 'Draf Terbaru Saya',
     'my_recently_viewed' => 'Baru saja saya lihat',
+    'my_most_viewed_favourites' => 'Favorit Saya yang Paling Banyak Dilihat',
+    'my_favourites' => 'Favoritku',
     'no_pages_viewed' => 'Anda belum melihat halaman apa pun',
     'no_pages_recently_created' => 'Tidak ada halaman yang baru saja dibuat',
     'no_pages_recently_updated' => 'Tidak ada halaman yang baru-baru ini diperbarui',
@@ -118,7 +120,7 @@ return [
     'books_delete_explain' => 'Ini akan menghapus buku dengan nama \': bookName\'. Semua halaman dan bab akan dihapus.',
     'books_delete_confirmation' => 'Apakah Anda yakin ingin menghapus buku ini?',
     'books_edit' => 'Edit Buku',
-    'books_edit_named' => 'Edit Buku :bookName',
+    'books_edit_named' => 'Sunting Buku :bookName',
     'books_form_book_name' => 'Nama Buku',
     'books_save' => 'Simpan Buku',
     'books_permissions' => 'Izin Buku',
@@ -152,7 +154,7 @@ return [
     'chapters_delete_explain' => 'Ini akan menghapus chapter dengan nama \':chapterName\'. Semua halaman yang ada dalam bab ini juga akan dihapus.',
     'chapters_delete_confirm' => 'Anda yakin ingin menghapus bab ini?',
     'chapters_edit' => 'Edit Bab',
-    'chapters_edit_named' => 'Edit Bab :chapterName',
+    'chapters_edit_named' => 'Sunting Bab :chapterName',
     'chapters_save' => 'Simpan Bab',
     'chapters_move' => 'Pindahkan Bab',
     'chapters_move_named' => 'Pindahkan Bab :chapterName',
@@ -179,7 +181,7 @@ return [
     'pages_delete_draft_success' => 'Halaman draf dihapus',
     'pages_delete_confirm' => 'Anda yakin ingin menghapus halaman ini?',
     'pages_delete_draft_confirm' => 'Anda yakin ingin menghapus halaman draf ini?',
-    'pages_editing_named' => 'Mengedit Halaman :pageName',
+    'pages_editing_named' => 'Menyunting Halaman :pageName',
     'pages_edit_draft_options' => 'Opsi Draf',
     'pages_edit_save_draft' => 'Simpan Draf',
     'pages_edit_draft' => 'Edit Halaman Draf',
@@ -188,7 +190,7 @@ return [
     'pages_edit_draft_save_at' => 'Draf disimpan pada ',
     'pages_edit_delete_draft' => 'Hapus Draf',
     'pages_edit_discard_draft' => 'Buang Draf',
-    'pages_edit_set_changelog' => 'Setel Changelog',
+    'pages_edit_set_changelog' => 'Atur Changelog',
     'pages_edit_enter_changelog_desc' => 'Masukkan deskripsi singkat tentang perubahan yang Anda buat',
     'pages_edit_enter_changelog' => 'Masuk ke Changelog',
     'pages_save' => 'Simpan Halaman',
@@ -224,20 +226,20 @@ return [
     'pages_revisions_restore' => 'Mengembalikan',
     'pages_revisions_none' => 'Halaman ini tidak memiliki revisi',
     'pages_copy_link' => 'Salin tautan',
-    'pages_edit_content_link' => 'Edit Konten',
+    'pages_edit_content_link' => 'Sunting Konten',
     'pages_permissions_active' => 'Izin Halaman Aktif',
     'pages_initial_revision' => 'Penerbitan awal',
     'pages_initial_name' => 'Halaman Baru',
-    'pages_editing_draft_notification' => 'Anda sedang mengedit draf yang terakhir disimpan :timeDiff.',
+    'pages_editing_draft_notification' => 'Anda sedang menyunting konsep yang terakhir disimpan :timeDiff.',
     'pages_draft_edited_notification' => 'Halaman ini telah diperbarui sejak saat itu. Anda disarankan untuk membuang draf ini.',
     'pages_draft_edit_active' => [
         'start_a' => ':count pengguna sudah mulai mengedit halaman ini',
-        'start_b' => ':userName sudah mulai mengedit halaman ini',
-        'time_a' => 'perubahan di sini disimpan secara instan',
+        'start_b' => ':userName telah memulai menyunting halaman ini',
+        'time_a' => 'semenjak halaman terakhir diperbarui',
         'time_b' => 'di akhir :minCount menit',
         'message' => ':start :time. Berhati-hatilah untuk tidak menimpa pembaruan satu sama lain!',
     ],
-    'pages_draft_discarded' => 'Draf dibuang, Editor telah diperbarui dengan konten halaman saat ini',
+    'pages_draft_discarded' => 'Konsep dibuang, Penyunting telah diperbarui dengan konten halaman saat ini',
     'pages_specific' => 'Halaman Tertentu',
     'pages_is_template' => 'Template Halaman',
 
@@ -254,29 +256,29 @@ return [
     'tags_add' => 'Tambahkan tag lain',
     'tags_remove' => 'Hapus tag ini',
     'attachments' => 'Lampiran',
-    'attachments_explain' => 'Unggah beberapa file atau lampirkan beberapa tautan untuk ditampilkan di laman Anda. Ini terlihat di sidebar halaman.',
+    'attachments_explain' => 'Unggah beberapa berkas atau lampirkan beberapa tautan untuk ditampilkan di laman Anda. Ini terlihat di sidebar halaman.',
     'attachments_explain_instant_save' => 'Perubahan di sini disimpan secara instan.',
     'attachments_items' => 'Item Terlampir',
-    'attachments_upload' => 'Unggah File',
+    'attachments_upload' => 'Unggah Berkas',
     'attachments_link' => 'Lampirkan Tautan',
     'attachments_set_link' => 'Setel Tautan',
     'attachments_delete' => 'Anda yakin ingin menghapus lampiran ini?',
     'attachments_dropzone' => 'Jatuhkan file atau klik di sini untuk melampirkan file',
-    'attachments_no_files' => 'Tidak ada file yang telah diunggah',
-    'attachments_explain_link' => 'Anda dapat melampirkan link jika Anda memilih untuk tidak mengupload file. Ini bisa berupa tautan ke halaman lain atau tautan ke file di cloud.',
+    'attachments_no_files' => 'Tidak ada berkas yang telah diunggah',
+    'attachments_explain_link' => 'Anda dapat melampirkan sebuah tautan jika Anda memilih untuk tidak mengunggah berkas. Ini bisa berupa sebuah tautan ke halaman lain atau tautan ke sebuah berkas di cloud.',
     'attachments_link_name' => 'Nama Tautan',
     'attachment_link' => 'Lampiran Tautan',
     'attachments_link_url' => 'Tautan ke file',
-    'attachments_link_url_hint' => 'Url situs atau file',
+    'attachments_link_url_hint' => 'Alamat url situs atau berkas',
     'attach' => 'Melampirkan',
     'attachments_insert_link' => 'Tambahkan Tautan Lampiran ke Halaman',
     'attachments_edit_file' => 'Edit File',
     'attachments_edit_file_name' => 'Nama file',
-    'attachments_edit_drop_upload' => 'Lepaskan file atau klik di sini untuk mengupload dan menimpa',
+    'attachments_edit_drop_upload' => 'Jatuhkan berkas atau klik di sini untuk mengunggah dan menimpa',
     'attachments_order_updated' => 'Urutan lampiran diperbarui',
     'attachments_updated_success' => 'Detail lampiran diperbarui',
     'attachments_deleted' => 'Lampiran dihapus',
-    'attachments_file_uploaded' => 'File berhasil diunggah',
+    'attachments_file_uploaded' => 'Berkas berhasil diunggah',
     'attachments_file_updated' => 'File berhasil diperbarui',
     'attachments_link_attached' => 'Tautan berhasil dilampirkan ke halaman',
     'templates' => 'Template',
index ec3564ece1c572099e672dabb66e3c1b6a08ac41..9244c96e17de2823d4aef3c82429dea2cacad76b 100644 (file)
@@ -15,42 +15,42 @@ return [
     'email_confirmation_expired' => 'Token konfirmasi telah kedaluwarsa, Email konfirmasi baru telah dikirim.',
     'email_confirmation_awaiting' => 'Alamat email untuk akun yang digunakan perlu dikonfirmasi',
     'ldap_fail_anonymous' => 'Akses LDAP gagal menggunakan pengikatan anonim',
-    'ldap_fail_authed' => 'Akses LDAP gagal menggunakan detail dn & sandi yang diberikan',
+    'ldap_fail_authed' => 'Akses LDAP gagal menggunakan rincian dn & sandi yang diberikan',
     'ldap_extension_not_installed' => 'Ekstensi LDAP PHP tidak terpasang',
     'ldap_cannot_connect' => 'Tidak dapat terhubung ke server ldap, Koneksi awal gagal',
     'saml_already_logged_in' => 'Telah masuk',
     'saml_user_not_registered' => 'Pengguna :name tidak terdaftar dan pendaftaran otomatis dinonaktifkan',
-    'saml_no_email_address' => 'Tidak dapat menemukan alamat email untuk pengguna ini dalam data yang diberikan oleh sistem autentikasi eksternal',
-    'saml_invalid_response_id' => 'Permintaan dari sistem otentikasi eksternal tidak dikenali oleh proses yang dimulai oleh aplikasi ini. Menavigasi kembali setelah masuk dapat menyebabkan masalah ini.',
-    'saml_fail_authed' => 'Login menggunakan :system gagal, sistem tidak memberikan otorisasi yang berhasil',
+    'saml_no_email_address' => 'Tidak dapat menemukan sebuah alamat email untuk pengguna ini, dalam data yang diberikan oleh sistem autentikasi eksternal',
+    'saml_invalid_response_id' => 'Permintaan dari sistem otentikasi eksternal tidak dikenali oleh sebuah proses yang dimulai oleh aplikasi ini. Menavigasi kembali setelah masuk dapat menyebabkan masalah ini.',
+    'saml_fail_authed' => 'Masuk menggunakan :system gagal, sistem tidak memberikan otorisasi yang berhasil',
     'social_no_action_defined' => 'Tidak ada tindakan yang ditentukan',
-    'social_login_bad_response' => "Kesalahan diterima selama :socialAccount :\n:error",
-    'social_account_in_use' => 'Ini:socialAccount sudah digunakan, Coba masuk melalui opsi :socialAccount.',
+    'social_login_bad_response' => "Kesalahan yang diterima selama masuk menggunakan :socialAccount : \n:error",
+    'social_account_in_use' => 'Akun :socialAccount ini sudah digunakan, Coba masuk melalui opsi :socialAccount.',
     'social_account_email_in_use' => 'Email :email sudah digunakan. Jika Anda sudah memiliki akun, Anda dapat menghubungkan :socialAccount Anda dari pengaturan profil Anda.',
-    'social_account_existing' => 'Akun ini :socialAccount sudah dilampirkan ke profil Anda.',
-    'social_account_already_used_existing' => 'Akun ini :socialAccount sudah digunakan oleh pengguna lain.',
-    'social_account_not_used' => 'Akun :socialAccount tidak ditautkan ke pengguna mana pun. Harap lampirkan di pengaturan profil Anda. ',
+    'social_account_existing' => 'Akun :socialAccount ini sudah dilampirkan ke profil Anda.',
+    'social_account_already_used_existing' => 'Akun :socialAccount ini sudah digunakan oleh pengguna lain.',
+    'social_account_not_used' => 'Akun :socialAccount ini tidak ditautkan ke pengguna mana pun. Harap lampirkan di dalam pengaturan profil Anda. ',
     'social_account_register_instructions' => 'Jika Anda belum memiliki akun, Anda dapat mendaftarkan akun menggunakan opsi :socialAccount.',
     'social_driver_not_found' => 'Pengemudi sosial tidak ditemukan',
     'social_driver_not_configured' => 'Pengaturan sosial :socialAccount Anda tidak dikonfigurasi dengan benar.',
     'invite_token_expired' => 'Tautan undangan ini telah kedaluwarsa. Sebagai gantinya, Anda dapat mencoba mengatur ulang kata sandi akun Anda.',
 
     // System
-    'path_not_writable' => 'Jalur file :filePath tidak dapat diunggah ke. Pastikan itu dapat ditulis ke server.',
-    'cannot_get_image_from_url' => 'Tidak bisa mendapatkan gambar dari :url',
+    'path_not_writable' => 'Jalur berkas :filePath tidak dapat diunggah. Pastikan berkas tersebut dapat ditulis ke server.',
+    'cannot_get_image_from_url' => 'Tidak dapat mengambil gambar dari :url',
     'cannot_create_thumbs' => 'Server tidak dapat membuat thumbnail. Harap periksa apakah Anda telah memasang ekstensi GD PHP.',
-    'server_upload_limit' => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran file yang lebih kecil.',
-    'uploaded'  => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran file yang lebih kecil.',
-    'image_upload_error' => 'Terjadi kesalahan saat mengupload gambar',
+    'server_upload_limit' => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran berkas yang lebih kecil.',
+    'uploaded'  => 'Server tidak mengizinkan unggahan dengan ukuran ini. Harap coba ukuran berkas yang lebih kecil.',
+    'image_upload_error' => 'Terjadi kesalahan saat mengunggah gambar',
     'image_upload_type_error' => 'Jenis gambar yang diunggah tidak valid',
-    'file_upload_timeout' => 'Waktu unggah file telah habis.',
+    'file_upload_timeout' => 'Unggahan berkas telah habis waktu.',
 
     // Attachments
     'attachment_not_found' => 'Lampiran tidak ditemukan',
 
     // Pages
     'page_draft_autosave_fail' => 'Gagal menyimpan draf. Pastikan Anda memiliki koneksi internet sebelum menyimpan halaman ini',
-    'page_custom_home_deletion' => 'Tidak dapat menghapus halaman saat disetel sebagai beranda',
+    'page_custom_home_deletion' => 'Tidak dapat menghapus sebuah halaman saat diatur sebagai sebuah halaman beranda',
 
     // Entities
     'entity_not_found' => 'Entitas tidak ditemukan',
@@ -67,7 +67,7 @@ return [
     'users_cannot_delete_guest' => 'Anda tidak dapat menghapus pengguna tamu',
 
     // Roles
-    'role_cannot_be_edited' => 'Peran ini tidak dapat diedit',
+    'role_cannot_be_edited' => 'Peran ini tidak dapat disunting',
     'role_system_cannot_be_deleted' => 'Peran ini adalah peran sistem dan tidak dapat dihapus',
     'role_registration_default_cannot_delete' => 'Peran ini tidak dapat dihapus jika disetel sebagai peran pendaftaran default',
     'role_cannot_remove_only_admin' => 'Pengguna ini adalah satu-satunya pengguna yang ditetapkan ke peran administrator. Tetapkan peran administrator untuk pengguna lain sebelum mencoba untuk menghapusnya di sini.',
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Halaman tidak ditemukan',
     'sorry_page_not_found' => 'Maaf, Halaman yang Anda cari tidak dapat ditemukan.',
     'sorry_page_not_found_permission_warning' => 'Jika Anda mengharapkan halaman ini ada, Anda mungkin tidak memiliki izin untuk melihatnya.',
+    'image_not_found' => 'Gambar tidak ditemukan',
+    'image_not_found_subtitle' => 'Maaf, Berkas gambar yang Anda cari tidak dapat ditemukan.',
+    'image_not_found_details' => 'Jika Anda mengharapkan gambar ini ada, gambar itu mungkin telah dihapus.',
     'return_home' => 'Kembali ke home',
     'error_occurred' => 'Terjadi kesalahan',
     'app_down' => ':appName sedang down sekarang',
index 7f83fbad885155c1c278dc66496f348ae7586a7e..80e8d38e15776f7df9c1f89f68472ac909d05949 100644 (file)
@@ -214,7 +214,7 @@ return [
     'user_api_token_update_success' => 'Token API berhasil diperbarui',
     'user_api_token' => 'Token API',
     'user_api_token_id' => 'Token ID',
-    'user_api_token_id_desc' => 'Ini adalah pengenal yang dibuat oleh sistem yang tidak dapat diedit untuk token ini yang perlu disediakan dalam permintaan API.',
+    'user_api_token_id_desc' => 'Ini adalah sebuah pengenal yang dihasilkan oleh sistem yang tidak dapat disunting untuk token ini yang perlu untuk disediakan dalam permintaan API.',
     'user_api_token_secret' => 'Token Secret',
     'user_api_token_secret_desc' => 'Ini adalah rahasia yang dihasilkan sistem untuk token ini yang perlu disediakan dalam permintaan API. Ini hanya akan ditampilkan kali ini jadi salin nilai ini ke tempat yang aman dan terlindungi.',
     'user_api_token_created' => 'Token dibuat :timeAgo',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 10ac2900ece859b63ee09c08ac36cbca3715ebcd..2bd5cf7331a3373ae2ed3bdf88268961130c6038 100644 (file)
@@ -100,7 +100,7 @@ return [
     'timezone'             => ':attribute harus menjadi zona yang valid.',
     'unique'               => ':attribute sudah diambil.',
     'url'                  => ':attribute format tidak valid.',
-    'uploaded'             => 'File tidak dapat diunggah. Server mungkin tidak menerima file dengan ukuran ini.',
+    'uploaded'             => 'Berkas tidak dapat diunggah. Server mungkin tidak menerima berkas dengan ukuran ini.',
 
     // Custom validation lines
     'custom' => [
index 24b484181ef75f34c16350b0ee454163dd0a1e73..11c52b696f403c27d8fbbbf63234be75705f49aa 100755 (executable)
@@ -6,7 +6,7 @@
 return [
 
     // Pages
-    'page_create'                 => 'ha creato la pagina',
+    'page_create'                 => 'pagina creata',
     'page_create_notification'    => 'Pagina Creata Correttamente',
     'page_update'                 => 'ha aggiornato la pagina',
     'page_update_notification'    => 'Pagina Aggiornata Correttamente',
@@ -43,7 +43,11 @@ return [
     'bookshelf_delete'                 => 'ha eliminato la libreria',
     'bookshelf_delete_notification'    => 'Libreria Eliminata Correttamente',
 
+    // Favourites
+    'favourite_add_notification' => '":name" è stato aggiunto ai tuoi preferiti',
+    'favourite_remove_notification' => '":name" è stato rimosso dai tuoi preferiti',
+
     // Other
     'commented_on'                => 'ha commentato in',
-    'permissions_update'          => 'updated permissions',
+    'permissions_update'          => 'autorizzazioni aggiornate',
 ];
index 6ffa1df5ef4aa17a92ced3f3140d340e475d7eed..f33aa4cd3b16a614b0346999e124c61603a5d1a4 100755 (executable)
@@ -19,7 +19,7 @@ return [
     'description' => 'Descrizione',
     'role' => 'Ruolo',
     'cover_image' => 'Immagine di copertina',
-    'cover_image_description' => 'Questa immagine dovrebbe essere approssimatamente 440x250px.',
+    'cover_image_description' => 'Questa immagine dovrebbe essere approssimativamente 440x250px.',
     
     // Actions
     'actions' => 'Azioni',
@@ -33,13 +33,17 @@ return [
     'copy' => 'Copia',
     'reply' => 'Rispondi',
     'delete' => 'Elimina',
-    'delete_confirm' => 'Confirm Deletion',
+    'delete_confirm' => 'Conferma Eliminazione',
     'search' => 'Cerca',
     'search_clear' => 'Pulisci Ricerca',
     'reset' => 'Azzera',
     'remove' => 'Rimuovi',
     'add' => 'Aggiungi',
     'fullscreen' => 'Schermo intero',
+    'favourite' => 'Aggiungi ai Preferiti',
+    'unfavourite' => 'Rimuovi dai preferiti',
+    'next' => 'Successivo',
+    'previous' => 'Precedente',
 
     // Sort Options
     'sort_options' => 'Opzioni Ordinamento',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Ordine Ascendente',
     'sort_descending' => 'Ordine Discendente',
     'sort_name' => 'Nome',
-    'sort_default' => 'Default',
+    'sort_default' => 'Predefinito',
     'sort_created_at' => 'Data Creazione',
     'sort_updated_at' => 'Data Aggiornamento',
 
@@ -65,7 +69,8 @@ return [
     'breadcrumb' => 'Navigazione',
 
     // Header
-    'profile_menu' => 'Menu del profilo',
+    'header_menu_expand' => 'Espandi Menù Intestazione',
+    'profile_menu' => 'Menù del profilo',
     'view_profile' => 'Visualizza Profilo',
     'edit_profile' => 'Modifica Profilo',
     'dark_mode' => 'Modalità Scura',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Mostra Informazioni Secondarie',
     'tab_content' => 'Contenuto',
+    'tab_content_label' => 'Tab: Mostra Contenuto Principale',
 
     // Email Content
     'email_action_help' => 'Se hai problemi nel cliccare il pulsante ":actionText", copia e incolla lo URL sotto nel tuo browser:',
@@ -81,6 +88,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'Norme sulla privacy',
+    'terms_of_service' => 'Condizioni del Servizio',
 ];
index aa1c8f4a68fea41b80bb5b590ec11ba72944b226..63637aa484fccf69450ce473aad73e8584767663 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_text' => 'Are you sure you want to delete this image?',
+    'image_delete_confirm_text' => 'Sei sicuro di voler eliminare questa immagine?',
     'image_select_image' => 'Seleziona Immagine',
     'image_dropzone' => 'Rilascia immagini o clicca qui per caricarle',
     'images_deleted' => 'Immagini Eliminate',
index a567936daeebfdb4ab61980381cb04a109196ad2..9d5e8c2ab20741363d3420ec6a2b54ff43031414 100755 (executable)
@@ -22,11 +22,13 @@ return [
     'meta_created_name' => 'Creato :timeLength da :user',
     'meta_updated' => 'Aggiornato :timeLength',
     'meta_updated_name' => 'Aggiornato :timeLength da :user',
-    'meta_owned_name' => 'Owned by :user',
+    'meta_owned_name' => 'Creati da :user',
     'entity_select' => 'Selezione Entità',
     'images' => 'Immagini',
     'my_recent_drafts' => 'Bozze Recenti',
     'my_recently_viewed' => 'Visti di recente',
+    'my_most_viewed_favourites' => 'I Miei Preferiti Più Visti',
+    'my_favourites' => 'I miei Preferiti',
     'no_pages_viewed' => 'Non hai visto nessuna pagina',
     'no_pages_recently_created' => 'Nessuna pagina è stata creata di recente',
     'no_pages_recently_updated' => 'Nessuna pagina è stata aggiornata di recente',
@@ -40,7 +42,7 @@ return [
     'permissions_intro' => 'Una volta abilitati, questi permessi avranno la priorità su tutti gli altri.',
     'permissions_enable' => 'Abilita Permessi Custom',
     'permissions_save' => 'Salva Permessi',
-    'permissions_owner' => 'Owner',
+    'permissions_owner' => 'Proprietario',
 
     // Search
     'search_results' => 'Risultati Ricerca',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Permessi impostati',
     'search_created_by_me' => 'Creati da me',
     'search_updated_by_me' => 'Aggiornati da me',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Creati da me',
     'search_date_options' => 'Opzioni Data',
     'search_updated_before' => 'Aggiornati prima del',
     'search_updated_after' => 'Aggiornati dopo il',
@@ -149,7 +151,7 @@ return [
     'chapters_create' => 'Crea un nuovo capitolo',
     'chapters_delete' => 'Elimina Capitolo',
     'chapters_delete_named' => 'Elimina il capitolo :chapterName',
-    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_delete_explain' => 'Procedendo si eliminerà il capitolo denominato \':chapterName\'. Anche le pagine in esso contenute saranno eliminate.',
     'chapters_delete_confirm' => 'Sei sicuro di voler eliminare questo capitolo?',
     'chapters_edit' => 'Elimina Capitolo',
     'chapters_edit_named' => 'Modifica il capitolo :chapterName',
@@ -211,7 +213,7 @@ return [
     'pages_revisions' => 'Versioni Pagina',
     'pages_revisions_named' => 'Versioni della pagina :pageName',
     'pages_revision_named' => 'Versione della pagina :pageName',
-    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revision_restored_from' => 'Ripristinato da #:id; :summary',
     'pages_revisions_created_by' => 'Creata Da',
     'pages_revisions_date' => 'Data Versione',
     'pages_revisions_number' => '#',
@@ -269,7 +271,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_insert_link' => 'Aggiungi Link Allegato alla Pagina',
     '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 3e48ad762a942488b3c1d8bf99269fa2e3fb092a..9a20c744b7501c358c681e67fbca0ae3976b0455 100755 (executable)
@@ -83,20 +83,23 @@ return [
     '404_page_not_found' => 'Pagina Non Trovata',
     'sorry_page_not_found' => 'La pagina che stavi cercando non è stata trovata.',
     'sorry_page_not_found_permission_warning' => 'Se pensi che questa pagina possa esistere, potresti non avere i permessi per visualizzarla.',
+    'image_not_found' => 'Immagine non trovata',
+    'image_not_found_subtitle' => 'Spiacente, l\'immagine che stai cercando non è stata trovata.',
+    'image_not_found_details' => 'Se ti aspettavi che questa immagine esistesse, potrebbe essere stata cancellata.',
     'return_home' => 'Ritorna alla home',
     'error_occurred' => 'C\'è Stato un errore',
     'app_down' => ':appName è offline',
     'back_soon' => 'Ritornerà presto.',
 
     // API errors
-    'api_no_authorization_found' => 'No authorization token found on the request',
+    'api_no_authorization_found' => 'Nessun token di autorizzazione trovato nella richiesta',
     'api_bad_authorization_format' => 'Un token di autorizzazione è stato trovato nella richiesta, ma il formato sembra non corretto',
-    '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_token_not_found' => 'Nessun token API valido è stato trovato nel token di autorizzazione fornito',
+    'api_incorrect_token_secret' => 'Il token segreto fornito per il token API utilizzato non è corretto',
     'api_user_no_api_permission' => 'Il proprietario del token API utilizzato non ha il permesso di effettuare chiamate API',
-    'api_user_token_expired' => 'The authorization token used has expired',
+    'api_user_token_expired' => 'Il token di autorizzazione utilizzato è scaduto',
 
     // Settings & Maintenance
-    'maintenance_test_email_failure' => 'Error thrown when sending a test email:',
+    'maintenance_test_email_failure' => 'Si è verificato un errore durante l\'invio di una e-mail di prova:',
 
 ];
index 604e02fe2c6b0a47298624b3db025737864a26af..7099d54f37d118cd043776b2cc06c70bd211bd53 100755 (executable)
@@ -8,8 +8,8 @@ return [
 
     'password' => 'La password deve avere almeno sei caratteri e corrispondere alla conferma.',
     'user' => "Non possiamo trovare un utente per quella mail.",
-    'token' => 'The password reset token is invalid for this email address.',
+    'token' => 'Il token per reimpostare la password non è valido per questo indirizzo email.',
     'sent' => 'Ti abbiamo inviato via mail il link per reimpostare la password!',
-    'reset' => 'La tua password è stata resettata!',
+    'reset' => 'La tua password è stata reimpostata!',
 
 ];
index 4f0414bafac1187d7e7be1f607db026046d15cd2..6aaca5a23206883039dc874bc1176ad503ac02e1 100755 (executable)
@@ -37,11 +37,11 @@ return [
     'app_homepage' => 'Homepage Applicazione',
     'app_homepage_desc' => 'Seleziona una pagina da mostrare nella home anzichè quella di default. I permessi della pagina sono ignorati per quella selezionata.',
     'app_homepage_select' => 'Seleziona una pagina',
-    'app_footer_links' => 'Footer Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
-    'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links' => 'Link in basso',
+    'app_footer_links_desc' => 'Aggiungi link da mostrare in basso nel sito. Questi saranno visibili in fondo alla maggior parte delle pagine, incluse quelle che non richiedono un autenticazione. Puoi usare l\'etichetta "trans::<chiave>" per utilizzare le traduzioni implementate nella piattaforma. Esempio: usando "trans::common.privacy_policy" mostrerà il testo tradotto "Norme sulla privacy" e "trans::common.terms_of_service" mostrerà il testo tradotto "Condizioni del Servizio".',
+    'app_footer_links_label' => 'Etichetta del Link',
+    'app_footer_links_url' => 'URL del Link',
+    'app_footer_links_add' => 'Aggiungi Link in basso',
     'app_disable_comments' => 'Disattiva commenti',
     'app_disable_comments_toggle' => 'Disabilita commenti',
     'app_disable_comments_desc' => 'Disabilita i commenti su tutte le pagine nell\'applicazione. I commenti esistenti non sono mostrati. ',
@@ -49,7 +49,7 @@ return [
     // Color settings
     'content_colors' => 'Colori del contenuto',
     'content_colors_desc' => 'Imposta i colori per tutti gli elementi nella gerarchia della pagina. È raccomandato scegliere colori con una luminosità simile a quelli di default per una maggiore leggibilità.',
-    'bookshelf_color' => 'Colore delle libreria',
+    'bookshelf_color' => 'Colore della libreria',
     'book_color' => 'Colore del libro',
     'chapter_color' => 'Colore del capitolo',
     'page_color' => 'Colore della Pagina',
@@ -61,7 +61,7 @@ return [
     'reg_enable_toggle' => 'Abilita registrazione',
     'reg_enable_desc' => 'Quando la registrazione è abilitata, l\utente sarà in grado di registrarsi all\'applicazione. Al momento della registrazione gli verrà associato un ruolo utente predefinito.',
     'reg_default_role' => 'Ruolo predefinito dopo la registrazione',
-    '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' => 'L\'opzione precedente viene ignorata se l\'autenticazione esterna tramite LDAP o SAML è attiva. Se l\'autenticazione (effettuata sul sistema esterno) sarà valida, gli account di eventuali membri non registrati saranno creati in automatico.',
     'reg_email_confirmation' => 'Conferma Email',
     'reg_email_confirmation_toggle' => 'Richiedi conferma email',
     'reg_confirm_email_desc' => 'Se la restrizione per dominio è usata la conferma della mail sarà richiesta e la scelta ignorata.',
@@ -73,7 +73,7 @@ return [
     'maint' => 'Manutenzione',
     'maint_image_cleanup' => 'Pulizia Immagini',
     'maint_image_cleanup_desc' => "Esegue la scansione del contenuto delle pagine e delle revisioni per verificare quali immagini e disegni sono attualmente in uso e quali immagini sono ridondanti. Assicurati di creare backup completo del database e delle immagini prima di eseguire la pulizia.",
-    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_delete_images_only_in_revisions' => 'Elimina anche le immagini che esistono solo nelle vecchie revisioni della pagina',
     'maint_image_cleanup_run' => 'Esegui Pulizia',
     'maint_image_cleanup_warning' => ':count immagini potenzialmente inutilizzate sono state trovate. Sei sicuro di voler eliminare queste immagini?',
     'maint_image_cleanup_success' => ':count immagini potenzialmente inutilizzate trovate e eliminate!',
@@ -85,41 +85,41 @@ return [
     'maint_send_test_email_mail_subject' => 'Email di Test',
     '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.',
-    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
+    'maint_recycle_bin_desc' => 'Le librerie, i libri, i capitoli e le pagine cancellati vengono inviati al cestino in modo che possano essere ripristinati o eliminati definitivamente. Gli elementi più vecchi nel cestino possono essere automaticamente rimossi dopo un certo periodo, a seconda della configurazione del sistema.',
     'maint_recycle_bin_open' => 'Apri il Cestino',
 
     // Recycle Bin
     'recycle_bin' => 'Cestino',
-    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
-    'recycle_bin_deleted_item' => 'Deleted Item',
+    'recycle_bin_desc' => 'Qui è possibile ripristinare gli elementi che sono stati eliminati o scegliere di rimuoverli definitivamente dal sistema. Questo elenco non è filtrato a differenza di elenchi di attività simili nel sistema in cui vengono applicati i filtri autorizzazioni.',
+    'recycle_bin_deleted_item' => 'Elimina Elemento',
     'recycle_bin_deleted_by' => 'Cancellato da',
     'recycle_bin_deleted_at' => 'Orario Cancellazione',
     'recycle_bin_permanently_delete' => 'Elimina Definitivamente',
     'recycle_bin_restore' => 'Ripristina',
-    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
+    'recycle_bin_contents_empty' => 'Al momento il cestino è vuoto',
     'recycle_bin_empty' => 'Svuota Cestino',
-    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
-    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
-    'recycle_bin_destroy_list' => 'Items to be Destroyed',
-    'recycle_bin_restore_list' => 'Items to be Restored',
-    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
-    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
-    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
-    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+    'recycle_bin_empty_confirm' => 'Questa operazione cancellerà definitivamente tutti gli elementi presenti nel cestino, inclusi i contenuti relativi a ciascun elemento. Sei sicuro di voler svuotare il cestino?',
+    'recycle_bin_destroy_confirm' => 'Questa operazione eliminerà permanentemente questo elemento (insieme a tutti i relativi elementi elencati qui sotto) dal sistema e non sarà più possibile recuperarlo. Sei sicuro di voler eliminare permanentemente questo elemento?',
+    'recycle_bin_destroy_list' => 'Elementi da Eliminare definitivamente',
+    'recycle_bin_restore_list' => 'Elementi da Ripristinare',
+    'recycle_bin_restore_confirm' => 'Questa azione ripristinerà l\'elemento eliminato, compresi gli elementi figli, nella loro posizione originale. Se la posizione originale è stata eliminata, ed è ora nel cestino, anche l\'elemento padre dovrà essere ripristinato.',
+    'recycle_bin_restore_deleted_parent' => 'L\'elemento padre di questo elemento è stato eliminato. Questo elemento rimarrà eliminato fino a che l\'elemento padre non sarà ripristinato.',
+    'recycle_bin_destroy_notification' => 'Eliminati :count elementi dal cestino.',
+    'recycle_bin_restore_notification' => 'Ripristinati :count elementi dal cestino.',
 
     // 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' => 'Registro di Controllo',
+    'audit_desc' => 'Questo registro di controllo mostra la lista delle attività registrate dal sistema. Questa lista, a differenza di altre liste del sistema a cui vengono applicate dei filtri, è integrale.',
+    'audit_event_filter' => 'Filtra Eventi',
+    'audit_event_filter_no_filter' => 'Nessun Filtro',
+    'audit_deleted_item' => 'Elimina Elemento',
+    'audit_deleted_item_name' => 'Nome: :name',
     'audit_table_user' => 'Utente',
     'audit_table_event' => 'Evento',
-    'audit_table_related' => 'Related Item or Detail',
-    'audit_table_date' => 'Activity Date',
-    'audit_date_from' => 'Date Range From',
-    'audit_date_to' => 'Date Range To',
+    'audit_table_related' => 'Elemento o Dettaglio correlato',
+    'audit_table_date' => 'Data attività',
+    'audit_date_from' => 'Dalla data',
+    'audit_date_to' => 'Alla data',
 
     // Role Settings
     'roles' => 'Ruoli',
@@ -143,10 +143,10 @@ return [
     'role_manage_entity_permissions' => 'Gestire tutti i permessi di libri, capitoli e pagine',
     'role_manage_own_entity_permissions' => 'Gestire i permessi sui propri libri, capitoli e pagine',
     'role_manage_page_templates' => 'Gestisci template pagine',
-    'role_access_api' => 'Access system API',
+    'role_access_api' => 'API sistema d\'accesso',
     '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.',
+    'roles_system_warning' => 'Siate consapevoli che l\'accesso a uno dei tre permessi qui sopra, può consentire a un utente di modificare i propri privilegi o i privilegi di altri nel sistema. Assegna ruoli con questi permessi solo ad utenti fidati.',
     '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',
@@ -162,7 +162,7 @@ return [
     'user_profile' => 'Profilo Utente',
     'users_add_new' => 'Aggiungi Nuovo Utente',
     'users_search' => 'Cerca Utenti',
-    'users_latest_activity' => 'Latest Activity',
+    'users_latest_activity' => 'Ultima Attività',
     'users_details' => 'Dettagli Utente',
     'users_details_desc' => 'Imposta un nome e un indirizzo email per questo utente. L\'indirizzo email verrà utilizzato per accedere all\'applicazione.',
     'users_details_desc_no_email' => 'Imposta un nome per questo utente così gli altri possono riconoscerlo.',
@@ -180,8 +180,8 @@ return [
     'users_delete_named' => 'Elimina l\'utente :userName',
     'users_delete_warning' => 'Questo eliminerà completamente l\'utente \':userName\' dal sistema.',
     'users_delete_confirm' => 'Sei sicuro di voler eliminare questo utente?',
-    'users_migrate_ownership' => 'Migrate Ownership',
-    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
+    'users_migrate_ownership' => 'Cambia Proprietario',
+    'users_migrate_ownership_desc' => 'Seleziona qui un utente se vuoi che un altro utente diventi il proprietario di tutti gli elementi attualmente di proprietà di questo utente.',
     'users_none_selected' => 'Nessun utente selezionato',
     'users_delete_success' => 'Utente rimosso con successo',
     'users_edit' => 'Modifica Utente',
@@ -197,11 +197,11 @@ return [
     'users_social_disconnect' => 'Disconnetti Account',
     'users_social_connected' => 'L\'account :socialAccount è stato connesso correttamente al tuo profilo.',
     'users_social_disconnected' => 'L\'account :socialAccount è stato disconnesso correttamente dal tuo profilo.',
-    'users_api_tokens' => 'API Tokens',
+    'users_api_tokens' => 'Token API',
     'users_api_tokens_none' => 'No API tokens have been created for this user',
     'users_api_tokens_create' => 'Crea Token',
     'users_api_tokens_expires' => 'Scade',
-    'users_api_tokens_docs' => 'API Documentation',
+    'users_api_tokens_docs' => 'Documentazione API',
 
     // API Tokens
     'user_api_token_create' => 'Crea Token API',
@@ -210,17 +210,17 @@ return [
     '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_success' => 'API token successfully created',
-    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token_create_success' => 'Token API creato correttamente',
+    'user_api_token_update_success' => 'Token API aggiornato correttamente',
     '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' => 'Token Segreto',
     '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 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_warning' => 'Questa operazione eliminerà irreversibilmente dal sistema il token API denominato \':tokenName\'.',
     'user_api_token_delete_confirm' => 'Sei sicuri di voler eliminare questo token API?',
     'user_api_token_delete_success' => 'Token API eliminato correttamente',
 
@@ -232,7 +232,7 @@ return [
         'ar' => 'العربية',
         'bg' => 'Bǎlgarski',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Catalano',
         'cs' => 'Česky',
         'da' => 'Danese',
         'de' => 'Deutsch (Sie)',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index bb602619cea562e94c620f9d7f7ddcde602b640d..69d023688ea0357270b3ba007e8091f53ca47005 100755 (executable)
@@ -89,7 +89,7 @@ return [
     'required_without'     => 'Il campo :attribute è richiesto quando :values non è presente.',
     'required_without_all' => 'Il campo :attribute è richiesto quando nessuno dei :values sono presenti.',
     'same'                 => ':attribute e :other devono corrispondere.',
-    'safe_url'             => 'The provided link may not be safe.',
+    'safe_url'             => 'Il link inserito potrebbe non essere sicuro.',
     'size'                 => [
         'numeric' => 'Il campo :attribute deve essere :size.',
         'file'    => 'Il campo :attribute deve essere :size kilobytes.',
index b1995a654f5b8c85b8ef39cdbf9c9fb85762fcc5..fd119a30472feb3db4db04431d4ab9929cf64b78 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'ブックが削除されました。',
     'bookshelf_delete_notification'    => '本棚を削除しました',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'コメントする',
     'permissions_update'          => 'updated permissions',
index 6d406f8f4666ad060f8538ddad57f68ebd9769d5..e52da90a176ec332d46374a94c0184597c0a29dc 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => '削除',
     'add' => '追加',
     'fullscreen' => 'Fullscreen',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sort Options',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profile Menu',
     'view_profile' => 'プロフィール表示',
     'edit_profile' => 'プロフィール編集',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Content',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => '":actionText" をクリックできない場合、以下のURLをコピーしブラウザで開いてください:',
index 8760b25a3f70877be28c6869d0e62a697853372c..6515305e478608c0b3a04bf220f574cb9d411abc 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => '画像',
     'my_recent_drafts' => '最近の下書き',
     'my_recently_viewed' => '閲覧履歴',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'なにもページを閲覧していません',
     'no_pages_recently_created' => '最近作成されたページはありません',
     'no_pages_recently_updated' => '最近更新されたページはありません。',
index 983e07a3a524aed553fb875fa070aed5a466b0d1..4d1776f1296380e980e2eac7346f3c0c8dd15e38 100644 (file)
@@ -83,6 +83,9 @@ return [
     '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.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'ホームに戻る',
     'error_occurred' => 'エラーが発生しました',
     'app_down' => ':appNameは現在停止しています',
index a0f662fdf5511d30cd82c282d0e82feebf43da1e..b972c9b61a4151178962c9b8e0791d08534acfed 100644 (file)
@@ -12,18 +12,18 @@ return [
     'settings_save_success' => '設定を保存しました',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
+    'app_customization' => 'カスタマイズ',
+    'app_features_security' => '機能とセキュリティ',
     'app_name' => 'アプリケーション名',
     'app_name_desc' => 'この名前はヘッダーやEメール内で表示されます。',
     'app_name_header' => 'ヘッダーにアプリケーション名を表示する',
     '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',
+    'app_public_access_desc' => 'このオプションを有効にすると、ログインしていない訪問者があなたのBookStackインスタンスのコンテンツにアクセスできるようになります。',
+    'app_public_access_desc_guest' => '一般の訪問者のアクセスは、「ゲスト」ユーザー権限を通じて制御することができます。',
+    'app_public_access_toggle' => 'パブリックアクセスを許可',
     'app_public_viewing' => 'アプリケーションを公開する',
     'app_secure_images' => '画像アップロード時のセキュリティを強化',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
+    'app_secure_images_toggle' => 'より高いセキュリティの画像アップロードを可能にする',
     'app_secure_images_desc' => 'パフォーマンスの観点から、全ての画像が公開になっています。このオプションを有効にすると、画像URLの先頭にランダムで推測困難な文字列が追加され、アクセスを困難にします。',
     'app_editor' => 'ページエディタ',
     'app_editor_desc' => 'ここで選択されたエディタを全ユーザが使用します。',
@@ -37,10 +37,10 @@ return [
     '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' => 'ページを選択',
-    'app_footer_links' => 'Footer Links',
+    'app_footer_links' => 'フッタのリンク',
     'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
-    'app_footer_links_url' => 'Link URL',
+    'app_footer_links_label' => '表示するテキスト',
+    'app_footer_links_url' => 'リンク先の URL',
     'app_footer_links_add' => 'Add Footer Link',
     'app_disable_comments' => 'コメントを無効にする',
     'app_disable_comments_toggle' => 'コメントを無効にする',
@@ -48,7 +48,7 @@ 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' => 'Shelf Color',
     'book_color' => 'Book Color',
     'chapter_color' => 'Chapter Color',
@@ -57,13 +57,13 @@ return [
 
     // Registration Settings
     'reg_settings' => '登録設定',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
+    'reg_enable' => '登録を有効にする',
+    'reg_enable_toggle' => '登録を有効にする',
     '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' => '新規登録時のデフォルト役割',
-    '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' => '外部のLDAPまたはSAML認証が有効の場合、上記のオプションは無視されます。存在しないメンバーのユーザーアカウントは、使用している外部システムでの認証に成功した場合に自動的に作成されます。',
+    'reg_email_confirmation' => '確認メール',
+    'reg_email_confirmation_toggle' => 'メールによる確認を行う',
     'reg_confirm_email_desc' => 'ドメイン制限を有効にしている場合はEメール認証が必須となり、この項目は無視されます。',
     'reg_confirm_restrict_domain' => 'ドメイン制限',
     'reg_confirm_restrict_domain_desc' => '特定のドメインのみ登録できるようにする場合、以下にカンマ区切りで入力します。設定された場合、Eメール認証が必須になります。<br>登録後、ユーザは自由にEメールアドレスを変更できます。',
@@ -72,10 +72,10 @@ return [
     // Maintenance settings
     '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_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_image_cleanup_desc' => "ページや履歴の内容をスキャンして、どの画像や図面が現在使用されているか、どの画像が余っているかをチェックします。この機能を実行する前に、データベースと画像の完全なバックアップを作成してください。",
+    'maint_delete_images_only_in_revisions' => 'また、古いページのリビジョンにしか存在しない画像も削除します。',
     '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_warning' => ':count 個、使用されていない可能性のある画像が見つかりました。これらの画像を削除してもよろしいですか?',
     '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' => 'テストメールを送信',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index fda7e4ef35ebb21aeca7c763841254356f5c75c5..ed0b66112c6f0fbe6763e2a6458ed8fcdb56db99 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => '삭제된 서가',
     'bookshelf_delete_notification'    => '서가 지움',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => '댓글 쓰기',
     'permissions_update'          => 'updated permissions',
index 3a1bf6cb7a2526cde6f18d725ec57e28047a81fd..3cc46ab49e6fb0ebc993b24074e72b2a55c6b91a 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => '제거',
     'add' => '추가',
     'fullscreen' => '전체화면',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => '정렬 기준',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => '탐색 경로',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => '프로필',
     'view_profile' => '프로필 보기',
     'edit_profile' => '프로필 바꾸기',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => '정보',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => '내용',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => ':actionText를 클릭할 수 없을 때는 웹 브라우저에서 다음 링크로 접속할 수 있습니다.',
index e2fa0c7ae7c860c3b0a500dad7dc55c88c42ae5e..e286fee8ca1d4012f722424b5cfca56b515bcf49 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => '이미지',
     'my_recent_drafts' => '내 최근의 초안 문서',
     'my_recently_viewed' => '내가 읽은 문서',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => '문서 없음',
     'no_pages_recently_created' => '문서 없음',
     'no_pages_recently_updated' => '문서 없음',
index 093288c83a7f59f52c3db9dceec1475be5320bc4..b2a2c7a3a130214d2214e2797ead06d8abeec28a 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => '404 Not Found',
     'sorry_page_not_found' => '문서를 못 찾았습니다.',
     'sorry_page_not_found_permission_warning' => '이 페이지가 존재하기를 기대했다면, 볼 수 있는 권한이 없을 수 있다.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => '처음으로 돌아가기',
     'error_occurred' => '문제가 생겼습니다.',
     'app_down' => ':appName에 문제가 있는 것 같습니다',
index 920ce04506c6410e716a30cf02b14bf3c5b4b685..95a681eca16ed58fabee580e68e42c3b9ca6d997 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => '히브리어',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 8f99e0ff6d261dd349b6ad45de65d444d29a89f5..e424efa1d1b92845a7bd4e32e6dc05e32e464f2d 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'izdzēsa plauktu',
     'bookshelf_delete_notification'    => 'Plaukts Veiksmīgi Dzēsts',
 
+    // Favourites
+    'favourite_add_notification' => '":name" ir pievienots jūsu favorītiem',
+    'favourite_remove_notification' => '":name" ir izņemts no jūsu favorītiem',
+
     // Other
     'commented_on'                => 'komentēts',
     'permissions_update'          => 'atjaunoja atļaujas',
index 62157b70cdb1793c1464d5170ce12f7b3c8cb7e2..f99419c93e96767babbebcf442858fb4c0dcd15e 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Noņemt',
     'add' => 'Pievienot',
     'fullscreen' => 'Pilnekrāns',
+    'favourite' => 'Pievienot favorītiem',
+    'unfavourite' => 'Noņemt no favorītiem',
+    'next' => 'Nākamais',
+    'previous' => 'Iepriekšējais',
 
     // Sort Options
     'sort_options' => 'Kārtošanas Opcijas',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Navigācija',
 
     // Header
+    'header_menu_expand' => 'Izvērst galvenes izvēlni',
     'profile_menu' => 'Profila izvēlne',
     'view_profile' => 'Apskatīt profilu',
     'edit_profile' => 'Rediģēt profilu',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informācija',
+    'tab_info_label' => 'Tab: Rādīt sekundāro informāciju',
     'tab_content' => 'Saturs',
+    'tab_content_label' => 'Tab: Rādīt galveno saturu',
 
     // Email Content
     'email_action_help' => 'Ja ir problēmas noklikšķināt ":actionText" pogu, nokopē un ievieto saiti savā interneta pārlūkā:',
index 2639e225f3b220c8c14d2f7f164a59cb9ff17a19..0a01b9bf32522e2443b780cc450aca09f63ac615 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Attēli',
     'my_recent_drafts' => 'Mani melnraksti',
     'my_recently_viewed' => 'Mani nesen skatītie',
+    'my_most_viewed_favourites' => 'Mani biežāk skatītie favorīti',
+    'my_favourites' => 'Mani favorīti',
     'no_pages_viewed' => 'Neviena lapa vēl nav skatīta',
     'no_pages_recently_created' => 'Nav radīta neviena lapa',
     'no_pages_recently_updated' => 'Nav atjaunināta neviena lapa',
@@ -219,7 +221,7 @@ return [
     'pages_revisions_numbered_changes' => 'Revīzijas #:id izmaiņas',
     'pages_revisions_changelog' => 'Izmaiņu žurnāls',
     'pages_revisions_changes' => 'Izmaiņas',
-    'pages_revisions_current' => 'Tekošā versija',
+    'pages_revisions_current' => 'Pašreizējā versija',
     'pages_revisions_preview' => 'Priekšskatījums',
     'pages_revisions_restore' => 'Atjaunot',
     'pages_revisions_none' => 'Šai lapai nav revīziju',
@@ -314,7 +316,7 @@ return [
 
     // Revision
     'revision_delete_confirm' => 'Vai esat pārliecināts, ka vēlaties dzēst šo revīziju?',
-    'revision_restore_confirm' => 'Vai esat pārliecināts, ka vēlaties atjaunot šo revīziju? Tekošais lapas saturs tiks aizstāts.',
+    'revision_restore_confirm' => 'Vai tiešām vēlaties atjaunot šo revīziju? Pašreizējais lapas saturs tiks aizvietots.',
     'revision_delete_success' => 'Revīzija dzēsta',
-    'revision_cannot_delete_latest' => 'Nevar dzēst tekošo revīziju.'
+    'revision_cannot_delete_latest' => 'Nevar dzēst pašreizējo revīziju.'
 ];
index f5e55a603d10499423286ca9dec665c153778db3..c1746d65a67d749202c450d663beda0b621db3e5 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Lapa nav atrasta',
     'sorry_page_not_found' => 'Atvainojiet, meklētā lapa nav atrasta.',
     'sorry_page_not_found_permission_warning' => 'Ja šai lapai būtu bijis te jābūt, jums var nebūt pietiekamas piekļuves tiesības, lai to apskatītu.',
+    'image_not_found' => 'Attēls nav atrasts',
+    'image_not_found_subtitle' => 'Atvainojiet, meklētais attēla fails nav atrasts.',
+    'image_not_found_details' => 'Ja attēlam būtu jābūt pieejamam, iespējams, tas ir ticis izdzēsts.',
     'return_home' => 'Atgriezties uz sākumu',
     'error_occurred' => 'Radusies kļūda',
     'app_down' => ':appName pagaidām nav pieejams',
index decc25c865cc4918838c387890424bcca592e2fc..dc00a23b31b72c670dea0e20c00f678d51ea5010 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index bf23393abf464f531cd1f7e6f97a7c11ab6bb13f..12d0dba6217d35032dd4c31d79ba0c5ece4a1e5b 100644 (file)
@@ -44,6 +44,10 @@ return [
     'bookshelf_delete'                 => 'slettet bokhylle',
     'bookshelf_delete_notification'    => 'Bokhyllen ble slettet',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'kommenterte på',
     'permissions_update'          => 'oppdaterte tilganger',
index e3bd8eefd37b7ff706e1a0ffc7c6642d454990fc..3aadd805aeddacb1a4c05dc9cdb9e7db8dd0e256 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Fjern',
     'add' => 'Legg til',
     'fullscreen' => 'Fullskjerm',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sorteringsalternativer',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Brødsmuler',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profilmeny',
     'view_profile' => 'Vis profil',
     'edit_profile' => 'Endre Profile',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informasjon',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Innhold',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Om du har problemer med å trykke på «:actionText»-knappen, bruk nettadressen under for å gå direkte dit:',
index 9ce2d3cd14f0ac0fb25195b06e13d092b65819e7..0b3d1b4164a95a434747cc5734933cc70fa4b4d1 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Bilder',
     'my_recent_drafts' => 'Mine nylige utkast',
     'my_recently_viewed' => 'Mine nylige visninger',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Du har ikke sett på noen sider',
     'no_pages_recently_created' => 'Ingen sider har nylig blitt opprettet',
     'no_pages_recently_updated' => 'Ingen sider har nylig blitt oppdatert',
index 35cee29a453c8c25bd743897923dabd6cf1e18ef..971dbf1cadb2a3302c6b9a359ad2b745330933d0 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Siden finnes ikke',
     'sorry_page_not_found' => 'Beklager, siden du leter etter ble ikke funnet.',
     'sorry_page_not_found_permission_warning' => 'Hvis du forventet at denne siden skulle eksistere, har du kanskje ikke tillatelse til å se den.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Gå til hovedside',
     'error_occurred' => 'En feil oppsto',
     'app_down' => ':appName er nede for øyeblikket',
index 4bd35776d59e3b130abab83cfc802ca90dc60e70..1451cff7098f1890b762aabfc8d861b148c86e27 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 634ea1202f8837eeb83474c888ad0cc53c9fd633..fcbad2400d8c24cc16b733c70d0c7bbce146174e 100644 (file)
@@ -7,43 +7,47 @@ return [
 
     // Pages
     'page_create'                 => 'maakte pagina',
-    'page_create_notification'    => 'Pagina Succesvol Aangemaakt',
-    'page_update'                 => 'veranderde pagina',
-    'page_update_notification'    => 'Pagina Succesvol Bijgewerkt',
+    'page_create_notification'    => 'Pagina succesvol aangemaakt',
+    'page_update'                 => 'wijzigde pagina',
+    'page_update_notification'    => 'Pagina succesvol bijgewerkt',
     'page_delete'                 => 'verwijderde pagina',
-    'page_delete_notification'    => 'Pagina Succesvol Verwijderd',
+    'page_delete_notification'    => 'Pagina succesvol verwijderd',
     'page_restore'                => 'herstelde pagina',
-    'page_restore_notification'   => 'Pagina Succesvol Hersteld',
+    'page_restore_notification'   => 'Pagina succesvol hersteld',
     'page_move'                   => 'verplaatste pagina',
 
     // Chapters
     'chapter_create'              => 'maakte hoofdstuk',
-    'chapter_create_notification' => 'Hoofdstuk Succesvol Aangemaakt',
-    'chapter_update'              => 'veranderde hoofdstuk',
-    'chapter_update_notification' => 'Hoofdstuk Succesvol Bijgewerkt',
+    'chapter_create_notification' => 'Hoofdstuk succesvol aangemaakt',
+    'chapter_update'              => 'wijzigde hoofdstuk',
+    'chapter_update_notification' => 'Hoofdstuk succesvol bijgewerkt',
     'chapter_delete'              => 'verwijderde hoofdstuk',
-    'chapter_delete_notification' => 'Hoofdstuk Succesvol Verwijderd',
+    'chapter_delete_notification' => 'Hoofdstuk succesvol verwijderd',
     'chapter_move'                => 'verplaatste hoofdstuk',
 
     // Books
     'book_create'                 => 'maakte boek',
-    'book_create_notification'    => 'Boek Succesvol Aangemaakt',
-    'book_update'                 => 'veranderde boek',
-    'book_update_notification'    => 'Boek Succesvol Bijgewerkt',
+    'book_create_notification'    => 'Boek succesvol aangemaakt',
+    'book_update'                 => 'wijzigde boek',
+    'book_update_notification'    => 'Boek succesvol bijgewerkt',
     'book_delete'                 => 'verwijderde boek',
-    'book_delete_notification'    => 'Boek Succesvol Verwijderd',
+    'book_delete_notification'    => 'Boek succesvol verwijderd',
     'book_sort'                   => 'sorteerde boek',
-    'book_sort_notification'      => 'Boek Succesvol Gesorteerd',
+    'book_sort_notification'      => 'Boek succesvol gesorteerd',
 
     // Bookshelves
-    'bookshelf_create'            => 'maakte Boekenplank',
-    'bookshelf_create_notification'    => 'Boekenplank Succesvol Aangemaakt',
-    'bookshelf_update'                 => 'veranderde boekenplank',
-    'bookshelf_update_notification'    => 'Boekenplank Succesvol Bijgewerkt',
+    'bookshelf_create'            => 'maakte boekenplank',
+    'bookshelf_create_notification'    => 'Boekenplank succesvol aangemaakt',
+    'bookshelf_update'                 => 'wijzigde boekenplank',
+    'bookshelf_update_notification'    => 'Boekenplank succesvol bijgewerkt',
     'bookshelf_delete'                 => 'verwijderde boekenplank',
-    'bookshelf_delete_notification'    => 'Boekenplank Succesvol Verwijderd',
+    'bookshelf_delete_notification'    => 'Boekenplank succesvol verwijderd',
+
+    // Favourites
+    'favourite_add_notification' => '":name" is toegevoegd aan je favorieten',
+    'favourite_remove_notification' => '":name" is verwijderd uit je favorieten',
 
     // Other
-    'commented_on'                => 'reactie op',
-    'permissions_update'          => 'updated permissions',
+    'commented_on'                => 'reageerde op',
+    'permissions_update'          => 'wijzigde permissies',
 ];
index ce0be87edfb2b201e640669591df021cfea536f7..fcff3fd48f0a5b8c4bdf4bbb29595c86dabe0b18 100644 (file)
@@ -7,7 +7,7 @@
 return [
 
     'failed' => 'Deze inloggegevens zijn niet bij ons bekend.',
-    'throttle' => 'Te veel loginpogingen! Probeer het opnieuw na :seconds seconden.',
+    'throttle' => 'Te veel login pogingen! Probeer het opnieuw na :seconds seconden.',
 
     // Login & Register
     'sign_up' => 'Registreren',
@@ -20,34 +20,34 @@ return [
     'username' => 'Gebruikersnaam',
     'email' => 'E-mail',
     'password' => 'Wachtwoord',
-    'password_confirm' => 'Wachtwoord Bevestigen',
+    'password_confirm' => 'Wachtwoord bevestigen',
     'password_hint' => 'Minimaal 8 tekens',
     'forgot_password' => 'Wachtwoord vergeten?',
     'remember_me' => 'Mij onthouden',
-    'ldap_email_hint' => 'Geef een email op waarmee je dit account wilt gebruiken.',
-    'create_account' => 'Account Aanmaken',
+    'ldap_email_hint' => 'Geef een emailadres op voor dit account.',
+    'create_account' => 'Account aanmaken',
     'already_have_account' => 'Heb je al een account?',
     'dont_have_account' => 'Nog geen account?',
     'social_login' => 'Aanmelden via een sociaal netwerk',
-    'social_registration' => 'Social Registratie',
-    'social_registration_text' => 'Registreer en log in met een andere dienst.',
+    'social_registration' => 'Social registratie',
+    'social_registration_text' => 'Registreer en log in met een andere service.',
 
     'register_thanks' => 'Bedankt voor het registreren!',
     'register_confirm' => 'Controleer je e-mail en bevestig je registratie om in te loggen op :appName.',
     'registrations_disabled' => 'Registratie is momenteel niet mogelijk',
     'registration_email_domain_invalid' => 'Dit e-maildomein is niet toegestaan',
-    'register_success' => 'Bedankt voor het inloggen. Je bent ook geregistreerd.',
+    'register_success' => 'Bedankt voor het aanmelden! Je bent nu geregistreerd en aangemeld.',
 
 
     // Password Reset
-    'reset_password' => 'Wachtwoord Herstellen',
+    'reset_password' => 'Wachtwoord herstellen',
     'reset_password_send_instructions' => 'Geef je e-mail en we sturen je een link om je wachtwoord te herstellen',
-    'reset_password_send_button' => 'Link Sturen',
+    'reset_password_send_button' => 'Link sturen',
     'reset_password_sent' => 'Een link om het wachtwoord te resetten zal verstuurd worden naar :email als dat e-mailadres in het systeem gevonden is.',
     'reset_password_success' => 'Je wachtwoord is succesvol hersteld.',
     'email_reset_subject' => 'Herstel je wachtwoord van :appName',
-    'email_reset_text' => 'Je ontvangt deze e-mail zodat je je wachtwoord kunt herstellen.',
-    'email_reset_not_requested' => 'Als je jouw wachtwoord niet wilt wijzigen, doe dan niets.',
+    'email_reset_text' => 'Je ontvangt deze e-mail omdat je een wachtwoord herstel verzoek had verzonden.',
+    'email_reset_not_requested' => 'Als je geen wachtwoord herstel hebt aangevraagd, hoef je niets te doen.',
 
 
     // Email Confirmation
@@ -56,14 +56,14 @@ return [
     'email_confirm_text' => 'Bevestig je registratie door op onderstaande knop te drukken:',
     'email_confirm_action' => 'Bevestig je e-mail',
     'email_confirm_send_error' => 'E-mail bevestiging is vereisd maar het systeem kon geen mail verzenden. Neem contact op met de beheerder.',
-    'email_confirm_success' => 'Je e-mailadres is bevestigt!',
+    'email_confirm_success' => 'Je e-mailadres is bevestigd!',
     'email_confirm_resent' => 'De bevestigingse-mails is opnieuw verzonden. Controleer je inbox.',
 
-    'email_not_confirmed' => 'E-mail nog niet bevestigd',
+    'email_not_confirmed' => 'E-mailadres nog niet bevestigd',
     'email_not_confirmed_text' => 'Je e-mailadres is nog niet bevestigd.',
     'email_not_confirmed_click_link' => 'Klik op de link in de e-mail die vlak na je registratie is verstuurd.',
     'email_not_confirmed_resend' => 'Als je deze e-mail niet kunt vinden kun je deze met onderstaande formulier opnieuw verzenden.',
-    'email_not_confirmed_resend_button' => 'Bevestigingsmail Opnieuw Verzenden',
+    'email_not_confirmed_resend_button' => 'Bevestigingsmail opnieuw verzenden',
 
     // User Invite
     'user_invite_email_subject' => 'Je bent uitgenodigd voor :appName!',
index 83f8b63d5865307d65380f4486023a16fa11267d..67d0876c7fe7ce023c441ce7444319fc5ad1eb86 100644 (file)
@@ -11,7 +11,7 @@ return [
     'save' => 'Opslaan',
     'continue' => 'Doorgaan',
     'select' => 'Kies',
-    'toggle_all' => 'Toggle Alles',
+    'toggle_all' => 'Toggle alles',
     'more' => 'Meer',
 
     // Form Labels
@@ -19,12 +19,12 @@ return [
     'description' => 'Beschrijving',
     'role' => 'Rol',
     'cover_image' => 'Omslagfoto',
-    'cover_image_description' => 'Deze afbeelding moet ongeveer 300x170px zijn.',
+    'cover_image_description' => 'Deze afbeelding moet ongeveer 440x250px zijn.',
     
     // Actions
     'actions' => 'Acties',
     'view' => 'Bekijk',
-    'view_all' => 'Bekijk Alle',
+    'view_all' => 'Bekijk alle',
     'create' => 'Aanmaken',
     'update' => 'Bijwerken',
     'edit' => 'Bewerk',
@@ -33,54 +33,61 @@ return [
     'copy' => 'Kopiëren',
     'reply' => 'Beantwoorden',
     'delete' => 'Verwijder',
-    'delete_confirm' => 'Confirm Deletion',
+    'delete_confirm' => 'Verwijdering bevestigen',
     'search' => 'Zoek',
     'search_clear' => 'Zoekopdracht wissen',
     'reset' => 'Resetten',
     'remove' => 'Verwijderen',
     'add' => 'Toevoegen',
     'fullscreen' => 'Volledig scherm',
+    'favourite' => 'Favoriet',
+    'unfavourite' => 'Verwijderen uit favoriet',
+    'next' => 'Volgende',
+    'previous' => 'Vorige',
 
     // Sort Options
     'sort_options' => 'Sorteeropties',
-    'sort_direction_toggle' => 'Sorteer richting',
+    'sort_direction_toggle' => 'Sorteerrichting',
     'sort_ascending' => 'Sorteer oplopend',
-    'sort_descending' => 'Sorteer teruglopend',
+    'sort_descending' => 'Sorteer aflopend',
     'sort_name' => 'Naam',
-    'sort_default' => 'Default',
+    'sort_default' => 'Standaard',
     'sort_created_at' => 'Aanmaakdatum',
     'sort_updated_at' => 'Gewijzigd op',
 
     // Misc
     'deleted_user' => 'Verwijderde gebruiker',
-    'no_activity' => 'Geen activiteiten',
+    'no_activity' => 'Geen activiteit om weer te geven',
     'no_items' => 'Geen items beschikbaar',
     'back_to_top' => 'Terug naar boven',
-    'toggle_details' => 'Details Weergeven',
-    'toggle_thumbnails' => 'Thumbnails Weergeven',
+    'toggle_details' => 'Details weergeven',
+    'toggle_thumbnails' => 'Thumbnails weergeven',
     'details' => 'Details',
     'grid_view' => 'Grid weergave',
-    'list_view' => 'Lijst weergave',
+    'list_view' => 'Lijstweergave',
     'default' => 'Standaard',
     'breadcrumb' => 'Kruimelpad',
 
     // Header
+    'header_menu_expand' => 'Header menu uitvouwen',
     'profile_menu' => 'Profiel menu',
-    'view_profile' => 'Profiel Weergeven',
-    'edit_profile' => 'Profiel Bewerken',
-    'dark_mode' => 'Donkere Modus',
-    'light_mode' => 'Lichte Modus',
+    'view_profile' => 'Profiel weergeven',
+    'edit_profile' => 'Profiel bewerken',
+    'dark_mode' => 'Donkere modus',
+    'light_mode' => 'Lichte modus',
 
     // Layout tabs
     'tab_info' => 'Info',
+    'tab_info_label' => 'Tabblad: Toon secundaire informatie',
     'tab_content' => 'Inhoud',
+    'tab_content_label' => 'Tabblad: Toon primaire inhoud',
 
     // Email Content
-    'email_action_help' => 'Als je de knop ":actionText" niet werkt, kopieer en plak de onderstaande URL in je web browser:',
+    'email_action_help' => 'Als je de knop ":actionText" niet werkt, kopieër en plak de onderstaande URL in je web browser:',
     'email_rights' => 'Alle rechten voorbehouden',
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'Privacybeleid',
+    'terms_of_service' => 'Algemene voorwaarden',
 ];
index f40ac15b0f4c23342b6a245d7328b936ee4ce133..0cddef7c79cc8dc435dc94b177e80d2ad534a6dd 100644 (file)
@@ -5,21 +5,21 @@
 return [
 
     // Image Manager
-    'image_select' => 'Selecteer Afbeelding',
+    'image_select' => 'Selecteer afbeelding',
     'image_all' => 'Alles',
     'image_all_title' => 'Alle afbeeldingen weergeven',
     'image_book_title' => 'Afbeeldingen van dit boek weergeven',
     'image_page_title' => 'Afbeeldingen van deze pagina weergeven',
     'image_search_hint' => 'Zoek op afbeeldingsnaam',
     'image_uploaded' => 'Geüpload :uploadedDate',
-    'image_load_more' => 'Meer Laden',
+    'image_load_more' => 'Meer laden',
     'image_image_name' => 'Afbeeldingsnaam',
     'image_delete_used' => 'Deze afbeeldingen is op onderstaande pagina\'s in gebruik.',
     'image_delete_confirm_text' => 'Weet u zeker dat u deze afbeelding wilt verwijderen?',
-    'image_select_image' => 'Kies Afbeelding',
+    'image_select_image' => 'Kies afbeelding',
     'image_dropzone' => 'Sleep afbeeldingen hier of klik hier om te uploaden',
-    'images_deleted' => 'Verwijderde Afbeeldingen',
-    'image_preview' => 'Afbeelding Voorbeeld',
+    'images_deleted' => 'Verwijderde afbeeldingen',
+    'image_preview' => 'Afbeelding voorbeeld',
     'image_upload_success' => 'Afbeelding succesvol geüpload',
     'image_update_success' => 'Afbeeldingsdetails succesvol verwijderd',
     'image_delete_success' => 'Afbeelding succesvol verwijderd',
@@ -27,8 +27,8 @@ return [
 
     // Code Editor
     'code_editor' => 'Code invoegen',
-    'code_language' => 'Code taal',
+    'code_language' => 'Codetaal',
     'code_content' => 'Code',
-    'code_session_history' => 'Zittingsgeschiedenis',
+    'code_session_history' => 'Sessie geschiedenis',
     'code_save' => 'Sla code op',
 ];
index 2d4bf0f4e1cac8a86f833e04eb6160d60877160a..eecdfd589b1f3c8edd158de45a26ac2cd3807a6c 100644 (file)
@@ -6,45 +6,47 @@
 return [
 
     // Shared
-    'recently_created' => 'Recent Aangemaakt',
-    'recently_created_pages' => 'Recent Aangemaakte Pagina\'s',
-    'recently_updated_pages' => 'Recent Bijgewerkte Pagina\'s',
-    'recently_created_chapters' => 'Recent Aangemaakte Hoofdstukken',
-    'recently_created_books' => 'Recent Aangemaakte Boeken',
-    'recently_created_shelves' => 'Recent Aangemaakte Boekenplanken',
-    'recently_update' => 'Recent Bijgewerkt',
-    'recently_viewed' => 'Recent Bekeken',
-    'recent_activity' => 'Recente Activiteit',
-    'create_now' => 'Maak er zelf één',
+    'recently_created' => 'Recent aangemaakt',
+    'recently_created_pages' => 'Recent aangemaakte pagina\'s',
+    'recently_updated_pages' => 'Recent bijgewerkte pagina\'s',
+    'recently_created_chapters' => 'Recent aangemaakte hoofdstukken',
+    'recently_created_books' => 'Recent aangemaakte boeken',
+    'recently_created_shelves' => 'Recent aangemaakte boekenplanken',
+    'recently_update' => 'Recent bijgewerkt',
+    'recently_viewed' => 'Recent bekeken',
+    'recent_activity' => 'Recente activiteit',
+    'create_now' => 'Maak er nu één',
     'revisions' => 'Revisies',
     'meta_revision' => 'Revisie #:revisionCount',
     'meta_created' => 'Aangemaakt :timeLength',
     'meta_created_name' => 'Aangemaakt: :timeLength door :user',
-    'meta_updated' => ':timeLength Aangepast',
+    'meta_updated' => 'Aangepast: :timeLength',
     'meta_updated_name' => 'Aangepast: :timeLength door :user',
-    'meta_owned_name' => 'Owned by :user',
-    'entity_select' => 'Entiteit Selecteren',
+    'meta_owned_name' => 'Eigendom van :user',
+    'entity_select' => 'Entiteit selecteren',
     'images' => 'Afbeeldingen',
-    'my_recent_drafts' => 'Mijn Concepten',
-    'my_recently_viewed' => 'Mijn Recent Bekeken',
+    'my_recent_drafts' => 'Mijn concepten',
+    'my_recently_viewed' => 'Mijn recent bekeken',
+    'my_most_viewed_favourites' => 'Mijn meest bekeken favorieten',
+    'my_favourites' => 'Mijn favorieten',
     'no_pages_viewed' => 'Je hebt nog niets bekeken',
     'no_pages_recently_created' => 'Er zijn geen recent aangemaakte pagina\'s',
     'no_pages_recently_updated' => 'Er zijn geen recente wijzigingen',
     'export' => 'Exporteren',
-    'export_html' => 'Ingesloten Webbestand',
-    'export_pdf' => 'PDF Bestand',
-    'export_text' => 'Normaal Tekstbestand',
+    'export_html' => 'Ingesloten webbestand',
+    'export_pdf' => 'PDF bestand',
+    'export_text' => 'Normaal tekstbestand',
 
     // Permissions and restrictions
     'permissions' => 'Permissies',
     'permissions_intro' => 'Als je dit aanzet, dan gelden rol-permissies niet meer voor deze pagina.',
-    'permissions_enable' => 'Custom Permissies Aanzetten',
-    'permissions_save' => 'Permissies Opslaan',
-    'permissions_owner' => 'Owner',
+    'permissions_enable' => 'Aangepaste permissies aanzetten',
+    'permissions_save' => 'Permissies opslaan',
+    'permissions_owner' => 'Eigenaar',
 
     // Search
     'search_results' => 'Zoekresultaten',
-    'search_total_results_found' => ':count resultaten gevonden|:count resultaten gevonden',
+    'search_total_results_found' => ':count resultaten gevonden|:count totaal aantal resultaten gevonden',
     'search_clear' => 'Zoekopdracht wissen',
     'search_no_pages' => 'Er zijn geen pagina\'s gevonden',
     'search_for_term' => 'Zoeken op :term',
@@ -52,16 +54,16 @@ return [
     'search_advanced' => 'Uitgebreid zoeken',
     'search_terms' => 'Zoektermen',
     'search_content_type' => 'Inhoudstype',
-    'search_exact_matches' => 'Exacte Matches',
+    'search_exact_matches' => 'Exacte matches',
     'search_tags' => 'Zoek tags',
     '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_permissions_set' => 'Permissies ingesteld',
     'search_created_by_me' => 'Door mij gemaakt',
     'search_updated_by_me' => 'Door mij geupdate',
-    'search_owned_by_me' => 'Owned by me',
-    'search_date_options' => 'Datum Opties',
+    'search_owned_by_me' => 'Eigendom van mij',
+    'search_date_options' => 'Datum opties',
     'search_updated_before' => 'Geupdate voor',
     'search_updated_after' => 'Geupdate na',
     'search_created_before' => 'Gecreëerd voor',
@@ -75,159 +77,159 @@ return [
     'x_shelves' => ':count Boekenplank|:count Boekenplanken',
     'shelves_long' => 'Boekenplanken',
     'shelves_empty' => 'Er zijn geen boekenplanken aangemaakt',
-    'shelves_create' => 'Nieuwe Boekenplank Aanmaken',
-    'shelves_popular' => 'Populaire Boekenplanken',
-    'shelves_new' => 'Nieuwe Boekenplanken',
-    'shelves_new_action' => 'Nieuwe Boekplank',
+    'shelves_create' => 'Nieuwe boekenplank maken',
+    'shelves_popular' => 'Populaire boekenplanken',
+    'shelves_new' => 'Nieuwe boekenplanken',
+    'shelves_new_action' => 'Nieuwe boekenplank',
     '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',
+    'shelves_save' => 'Boekenplank opslaan',
     'shelves_books' => 'Boeken op deze plank',
-    'shelves_add_books' => 'Toevoegen boeken aan deze plank',
-    'shelves_drag_books' => 'Sleep boeken hier naartoe om deze toe te voegen aan deze plank',
+    'shelves_add_books' => 'Voeg boeken toe aan deze plank',
+    'shelves_drag_books' => 'Sleep boeken hiernaartoe om deze toe te voegen aan deze plank',
     'shelves_empty_contents' => 'Er zijn geen boeken aan deze plank toegekend',
     'shelves_edit_and_assign' => 'Bewerk boekenplank om boeken toe te kennen.',
-    'shelves_edit_named' => 'Bewerk Boekenplank :name',
-    'shelves_edit' => 'Bewerk Boekenplank',
-    'shelves_delete' => 'Verwijder Boekenplank',
-    'shelves_delete_named' => 'Verwijder Boekenplank :name',
+    'shelves_edit_named' => 'Bewerk boekenplank :name',
+    'shelves_edit' => 'Bewerk boekenplank',
+    'shelves_delete' => 'Verwijder boekenplank',
+    'shelves_delete_named' => 'Verwijder boekenplank :name',
     'shelves_delete_explain' => "Deze actie verwijdert de boekenplank met naam ':name'. De boeken op deze plank worden niet verwijderd.",
     'shelves_delete_confirmation' => 'Weet je zeker dat je deze boekenplank wilt verwijderen?',
-    'shelves_permissions' => 'Boekenplank Permissies',
-    'shelves_permissions_updated' => 'Boekenplank Permissies Opgeslagen',
-    'shelves_permissions_active' => 'Boekenplank Permissies Actief',
-    'shelves_copy_permissions_to_books' => 'Kopieer Permissies naar Boeken',
-    'shelves_copy_permissions' => 'Kopieer Permissies',
-    'shelves_copy_permissions_explain' => 'Met deze actie worden de permissies van deze boekenplank gekopieerd naar alle boeken op de plank. Voordat deze actie wordt uitgevoerd, zorg dat de wijzigingen in de permissies van deze boekenplank zijn opgeslagen.',
-    'shelves_copy_permission_success' => 'Boekenplank permissies gekopieerd naar :count boeken',
+    'shelves_permissions' => 'Boekenplank permissies',
+    'shelves_permissions_updated' => 'Boekenplank permissies opgeslagen',
+    'shelves_permissions_active' => 'Boekenplank permissies actief',
+    'shelves_copy_permissions_to_books' => 'Kopieer permissies naar boeken',
+    'shelves_copy_permissions' => 'Kopieer permissies',
+    'shelves_copy_permissions_explain' => 'Met deze actie worden de permissies van deze boekenplank gekopieërd naar alle boeken op de plank. Voordat deze actie wordt uitgevoerd, zorg dat de wijzigingen in de permissies van deze boekenplank zijn opgeslagen.',
+    'shelves_copy_permission_success' => 'Boekenplank permissies gekopieërd naar :count boeken',
 
     // Books
     'book' => 'Boek',
     'books' => 'Boeken',
     'x_books' => ':count Boek|:count Boeken',
     'books_empty' => 'Er zijn geen boeken aangemaakt',
-    'books_popular' => 'Populaire Boeken',
-    'books_recent' => 'Recente Boeken',
-    'books_new' => 'Nieuwe Boeken',
-    'books_new_action' => 'Nieuw Boek',
+    'books_popular' => 'Populaire boeken',
+    'books_recent' => 'Recente boeken',
+    'books_new' => 'Nieuwe boeken',
+    'books_new_action' => 'Nieuw boek',
     'books_popular_empty' => 'De meest populaire boeken worden hier weergegeven.',
     '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',
+    'books_create' => 'Nieuw boek maken',
+    'books_delete' => 'Boek verwijderen',
+    'books_delete_named' => 'Verwijder boek :bookName',
     'books_delete_explain' => 'Deze actie verwijdert het boek \':bookName\', Alle pagina\'s en hoofdstukken worden verwijderd.',
     'books_delete_confirmation' => 'Weet je zeker dat je dit boek wilt verwijderen?',
-    'books_edit' => 'Boek Bewerken',
-    'books_edit_named' => 'Bewerkt Boek :bookName',
-    'books_form_book_name' => 'Boek Naam',
-    'books_save' => 'Boek Opslaan',
-    'books_permissions' => 'Boek Permissies',
-    'books_permissions_updated' => 'Boek Permissies Opgeslagen',
-    'books_empty_contents' => 'Er zijn nog een hoofdstukken en pagina\'s voor dit boek gemaakt.',
-    'books_empty_create_page' => 'Pagina Toevoegen',
+    'books_edit' => 'Boek bewerken',
+    'books_edit_named' => 'Bewerk boek :bookName',
+    'books_form_book_name' => 'Boek naam',
+    'books_save' => 'Boek opslaan',
+    'books_permissions' => 'Boek permissies',
+    'books_permissions_updated' => 'Boek permissies opgeslagen',
+    'books_empty_contents' => 'Er zijn nog geen hoofdstukken en pagina\'s voor dit boek gemaakt.',
+    'books_empty_create_page' => 'Nieuwe pagina maken',
     'books_empty_sort_current_book' => 'Boek sorteren',
-    'books_empty_add_chapter' => 'Hoofdstuk Toevoegen',
-    'books_permissions_active' => 'Boek Permissies Actief',
+    'books_empty_add_chapter' => 'Hoofdstuk toevoegen',
+    'books_permissions_active' => 'Boek permissies actief',
     'books_search_this' => 'Zoeken in dit boek',
-    'books_navigation' => 'Boek Navigatie',
+    'books_navigation' => 'Boek navigatie',
     'books_sort' => 'Inhoud van het boek sorteren',
-    'books_sort_named' => 'Sorteer Boek :bookName',
-    'books_sort_name' => 'Sorteren op Naam',
+    'books_sort_named' => 'Sorteer boek :bookName',
+    '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',
+    'books_sort_chapters_last' => 'Hoofdstukken laatst',
+    'books_sort_show_other' => 'Bekijk andere boeken',
+    'books_sort_save' => 'Nieuwe volgorde opslaan',
 
     // Chapters
     'chapter' => 'Hoofdstuk',
     'chapters' => 'Hoofdstukken',
     'x_chapters' => ':count Hoofdstuk|:count Hoofdstukken',
-    'chapters_popular' => 'Populaire Hoofdstukken',
-    'chapters_new' => 'Nieuw Hoofdstuk',
-    'chapters_create' => 'Hoofdstuk Toevoegen',
-    'chapters_delete' => 'Hoofdstuk Verwijderen',
-    'chapters_delete_named' => 'Verwijder Hoofdstuk :chapterName',
-    'chapters_delete_explain' => 'This will delete the chapter with the name \':chapterName\'. All pages that exist within this chapter will also be deleted.',
+    'chapters_popular' => 'Populaire hoofdstukken',
+    'chapters_new' => 'Nieuw hoofdstuk',
+    'chapters_create' => 'Nieuw hoofdstuk maken',
+    'chapters_delete' => 'Hoofdstuk verwijderen',
+    'chapters_delete_named' => 'Verwijder hoofdstuk :chapterName',
+    'chapters_delete_explain' => 'Dit verwijdert het hoofdstuk met de naam \':chapterName\'. Alle pagina\'s die binnen dit hoofdstuk staan, worden ook verwijderd.',
     'chapters_delete_confirm' => 'Weet je zeker dat je dit boek wilt verwijderen?',
-    'chapters_edit' => 'Hoofdstuk Aanpassen',
-    'chapters_edit_named' => 'Hoofdstuk :chapterName Aanpassen',
-    'chapters_save' => 'Hoofdstuk Opslaan',
-    'chapters_move' => 'Hoofdstuk Verplaatsen',
-    'chapters_move_named' => 'Verplaatst Hoofdstuk :chapterName',
-    'chapter_move_success' => 'Hoofdstuk Verplaatst Naar :bookName',
-    'chapters_permissions' => 'Hoofdstuk Permissies',
+    'chapters_edit' => 'Hoofdstuk aanpassen',
+    'chapters_edit_named' => 'Hoofdstuk :chapterName aanpassen',
+    'chapters_save' => 'Hoofdstuk opslaan',
+    'chapters_move' => 'Hoofdstuk verplaatsen',
+    'chapters_move_named' => 'Verplaatst hoofdstuk :chapterName',
+    'chapter_move_success' => 'Hoofdstuk verplaatst naar :bookName',
+    'chapters_permissions' => 'Hoofdstuk permissies',
     'chapters_empty' => 'Er zijn geen pagina\'s in dit hoofdstuk aangemaakt.',
-    'chapters_permissions_active' => 'Hoofdstuk Permissies Actief',
-    'chapters_permissions_success' => 'Hoofdstuk Permissies Bijgewerkt',
+    'chapters_permissions_active' => 'Hoofdstuk permissies actief',
+    'chapters_permissions_success' => 'Hoofdstuk permissies bijgewerkt',
     'chapters_search_this' => 'Doorzoek dit hoofdstuk',
 
     // Pages
     'page' => 'Pagina',
     'pages' => 'Pagina\'s',
     'x_pages' => ':count Pagina|:count Pagina\'s',
-    'pages_popular' => 'Populaire Pagina\'s',
-    'pages_new' => 'Nieuwe Pagina',
+    'pages_popular' => 'Populaire pagina\'s',
+    'pages_new' => 'Nieuwe pagina',
     'pages_attachments' => 'Bijlages',
-    'pages_navigation' => 'Pagina Navigatie',
-    'pages_delete' => 'Pagina Verwijderen',
-    'pages_delete_named' => 'Verwijderde Pagina :pageName',
-    'pages_delete_draft_named' => 'Verwijderde Conceptpagina :pageName',
-    'pages_delete_draft' => 'Verwijder Conceptpagina',
+    'pages_navigation' => 'Pagina navigatie',
+    'pages_delete' => 'Pagina verwijderen',
+    'pages_delete_named' => 'Verwijderd pagina :pageName',
+    'pages_delete_draft_named' => 'Verwijder concept pagina :pageName',
+    'pages_delete_draft' => 'Verwijder concept pagina',
     'pages_delete_success' => 'Pagina verwijderd',
     'pages_delete_draft_success' => 'Concept verwijderd',
     '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' => 'Concept Opties',
+    'pages_editing_named' => 'Pagina :pageName bewerken',
+    'pages_edit_draft_options' => 'Concept opties',
     'pages_edit_save_draft' => 'Concept opslaan',
-    'pages_edit_draft' => 'Paginaconcept Bewerken',
-    'pages_editing_draft' => 'Concept Bewerken',
-    'pages_editing_page' => 'Concept Bewerken',
+    'pages_edit_draft' => 'Paginaconcept bewerken',
+    'pages_editing_draft' => 'Concept bewerken',
+    'pages_editing_page' => 'Concept bewerken',
     'pages_edit_draft_save_at' => 'Concept opgeslagen op ',
-    'pages_edit_delete_draft' => 'Concept Verwijderen',
-    'pages_edit_discard_draft' => 'Concept Verwijderen',
+    'pages_edit_delete_draft' => 'Concept verwijderen',
+    'pages_edit_discard_draft' => 'Concept verwijderen',
     'pages_edit_set_changelog' => 'Changelog',
     'pages_edit_enter_changelog_desc' => 'Geef een korte omschrijving van de wijzingen die je gemaakt hebt.',
-    'pages_edit_enter_changelog' => 'Zie logboek',
-    'pages_save' => 'Pagina Opslaan',
-    'pages_title' => 'Pagina Titel',
-    'pages_name' => 'Pagina Naam',
-    'pages_md_editor' => 'Bewerker',
+    'pages_edit_enter_changelog' => 'Zie changelog',
+    'pages_save' => 'Pagina opslaan',
+    'pages_title' => 'Pagina titel',
+    'pages_name' => 'Pagina naam',
+    'pages_md_editor' => 'Bewerken',
     'pages_md_preview' => 'Voorbeeld',
-    'pages_md_insert_image' => 'Afbeelding Invoegen',
-    'pages_md_insert_link' => 'Entity Link Invoegen',
-    'pages_md_insert_drawing' => 'Tekening Toevoegen',
+    'pages_md_insert_image' => 'Afbeelding invoegen',
+    'pages_md_insert_link' => 'Entity link invoegen',
+    'pages_md_insert_drawing' => 'Tekening invoegen',
     'pages_not_in_chapter' => 'Deze pagina staat niet in een hoofdstuk',
-    'pages_move' => 'Pagina Verplaatsten',
+    'pages_move' => 'Pagina verplaatsten',
     'pages_move_success' => 'Pagina verplaatst naar ":parentName"',
-    '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_copy' => 'Pagina kopiëren',
+    'pages_copy_desination' => 'Kopieër bestemming',
+    'pages_copy_success' => 'Pagina succesvol gekopieërd',
+    'pages_permissions' => 'Pagina permissies',
+    'pages_permissions_success' => 'Pagina permissies bijgewerkt',
     'pages_revision' => 'Revisie',
-    'pages_revisions' => 'Pagina Revisies',
-    'pages_revisions_named' => 'Pagina Revisies voor :pageName',
-    'pages_revision_named' => 'Pagina Revisie voor :pageName',
-    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revisions' => 'Pagina revisies',
+    'pages_revisions_named' => 'Pagina revisies voor :pageName',
+    'pages_revision_named' => 'Pagina revisie voor :pageName',
+    'pages_revision_restored_from' => 'Hersteld van #:id; :samenvatting',
     'pages_revisions_created_by' => 'Aangemaakt door',
     'pages_revisions_date' => 'Revisiedatum',
     'pages_revisions_number' => '#',
     'pages_revisions_numbered' => 'Revisie #:id',
-    'pages_revisions_numbered_changes' => 'Revisie #:id Wijzigingen',
-    'pages_revisions_changelog' => 'Wijzigingslogboek',
+    'pages_revisions_numbered_changes' => 'Revisie #:id wijzigingen',
+    'pages_revisions_changelog' => 'Changelog',
     'pages_revisions_changes' => 'Wijzigingen',
-    'pages_revisions_current' => 'Huidige Versie',
+    'pages_revisions_current' => 'Huidige versie',
     'pages_revisions_preview' => 'Voorbeeld',
     'pages_revisions_restore' => 'Herstellen',
     'pages_revisions_none' => 'Deze pagina heeft geen revisies',
-    'pages_copy_link' => 'Link Kopiëren',
+    'pages_copy_link' => 'Link kopiëren',
     'pages_edit_content_link' => 'Bewerk inhoud',
-    'pages_permissions_active' => 'Pagina Permissies Actief',
+    'pages_permissions_active' => 'Pagina permissies actief',
     'pages_initial_revision' => 'Eerste publicatie',
-    'pages_initial_name' => 'Nieuwe Pagina',
+    'pages_initial_name' => 'Nieuwe pagina',
     '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' => [
@@ -237,40 +239,40 @@ return [
         'time_b' => 'in de laatste :minCount minuten',
         'message' => ':start :time. Let op om elkaars updates niet te overschrijven!',
     ],
-    'pages_draft_discarded' => 'Concept verwijderd, de editor is bijgewerkt met de huidige pagina-inhoud',
-    'pages_specific' => 'Specifieke Pagina',
+    'pages_draft_discarded' => 'Concept verwijderd, de editor is bijgewerkt met de huidige paginainhoud',
+    'pages_specific' => 'Specifieke pagina',
     'pages_is_template' => 'Paginasjabloon',
 
     // Editor Sidebar
     'page_tags' => 'Pagina Labels',
-    'chapter_tags' => 'Tags van Hoofdstuk',
-    'book_tags' => 'Tags van Boeken',
-    'shelf_tags' => 'Tags van Boekplanken',
+    'chapter_tags' => 'Labels van hoofdstuk',
+    'book_tags' => 'Labels van boeken',
+    'shelf_tags' => 'Labels van boekenplanken',
     'tag' => 'Label',
-    'tags' =>  'Tags',
-    'tag_name' =>  'Naam Tag',
-    'tag_value' => 'Label Waarde (Optioneel)',
+    'tags' =>  'Labels',
+    'tag_name' =>  'Naam label',
+    'tag_value' => 'Labelwaarde (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' => 'Deze tag verwijderen',
+    'tags_remove' => 'Dit label 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.',
     'attachments_items' => 'Bijlages',
-    'attachments_upload' => 'Bestand Uploaden',
-    'attachments_link' => 'Link Toevoegen',
-    'attachments_set_link' => 'Zet Link',
+    'attachments_upload' => 'Bestand uploaden',
+    'attachments_link' => 'Link toevoegen',
+    'attachments_set_link' => 'Zet link',
     '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.',
-    'attachments_link_name' => 'Link Naam',
+    'attachments_link_name' => 'Link naam',
     'attachment_link' => 'Bijlage link',
     'attachments_link_url' => 'Link naar bestand',
-    'attachments_link_url_hint' => 'Url, site of bestand',
+    'attachments_link_url_hint' => 'URL van site of bestand',
     'attach' => 'Koppelen',
-    'attachments_insert_link' => 'Add Attachment Link to Page',
-    'attachments_edit_file' => 'Bestand Bewerken',
+    'attachments_insert_link' => 'Bijlage link toevoegen aan pagina',
+    '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',
     'attachments_order_updated' => 'De volgorde van de bijlages is bijgewerkt',
@@ -297,7 +299,7 @@ return [
     // Comments
     'comment' => 'Reactie',
     'comments' => 'Reacties',
-    'comment_add' => 'Reactie Toevoegen',
+    '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 [
     'comment_deleted_success' => 'Reactie verwijderd',
     'comment_created_success' => 'Reactie toegevoegd',
     'comment_updated_success' => 'Reactie bijgewerkt',
-    'comment_delete_confirm' => 'Zeker reactie verwijderen?',
+    'comment_delete_confirm' => 'Weet je zeker dat je deze reactie wilt verwijderen?',
     'comment_in_reply_to' => 'Antwoord op :commentId',
 
     // Revision
index 585346748a8825958efd06396f6f9af8048c257c..7518314733ccde1352163b63eaf092cc5eec6cc2 100644 (file)
@@ -23,7 +23,7 @@ return [
     'saml_no_email_address' => 'Kan geen e-mailadres voor deze gebruiker vinden in de gegevens die door het externe verificatiesysteem worden verstrekt',
     'saml_invalid_response_id' => 'Het verzoek van het externe verificatiesysteem is niet herkend door een door deze applicatie gestart proces. Het terug navigeren na een login kan dit probleem veroorzaken.',
     'saml_fail_authed' => 'Inloggen met :system mislukt, het systeem gaf geen succesvolle autorisatie',
-    'social_no_action_defined' => 'Geen actie gedefineerd',
+    'social_no_action_defined' => 'Geen actie gedefineërd',
     'social_login_bad_response' => "Fout ontvangen tijdens :socialAccount login: \n:error",
     'social_account_in_use' => 'Dit :socialAccount account is al in gebruik, Probeer in te loggen met de :socialAccount optie.',
     'social_account_email_in_use' => 'Het e-mailadres :email is al in gebruik. Als je al een account hebt kun je een :socialAccount account verbinden met je profielinstellingen.',
@@ -83,6 +83,9 @@ return [
     '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' => 'Als u verwacht dat deze pagina bestaat heeft u misschien geen rechten om het te bekijken.',
+    'image_not_found' => 'Afbeelding niet gevonden',
+    'image_not_found_subtitle' => 'Sorry, de afbeelding die je zocht is niet beschikbaar.',
+    'image_not_found_details' => 'Als u verwachtte dat deze afbeelding zou bestaan, dan is deze misschien verwijderd.',
     'return_home' => 'Terug naar home',
     'error_occurred' => 'Er Ging Iets Fout',
     'app_down' => ':appName is nu niet beschikbaar',
index e5f7d0b5322ed103a5e654945de26e8b3d673c07..54cee27b13706b142a2208a512cb32ba61c4f4b1 100644 (file)
@@ -37,11 +37,11 @@ return [
     'app_homepage' => 'Applicatie Homepagina',
     'app_homepage_desc' => 'Selecteer een weergave om weer te geven op de homepage in plaats van de standaard weergave. Paginarechten worden genegeerd voor geselecteerde pagina\'s.',
     'app_homepage_select' => 'Selecteer een pagina',
-    'app_footer_links' => 'Footer Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
+    'app_footer_links' => 'Voettekst links',
+    'app_footer_links_desc' => 'Voeg links toe om te laten zien in de voettekst van de site. Deze worden onderaan de meeste pagina\'s weergegeven, met inbegrip van pagina\'s die geen inloggen vereisen. U kunt een label van "trans::<key>" gebruiken om systeemgedefinieerde vertalingen te gebruiken. Bijvoorbeeld: Het gebruik van "trans:common.privacy_policy" biedt de vertaalde tekst "Privacybeleid" en "trans:common.terms_of_service" voor de vertaalde tekst "Servicevoorwaarden".',
+    'app_footer_links_label' => 'Link label',
     'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links_add' => 'Voettekst link toevoegen',
     'app_disable_comments' => 'Reacties uitschakelen',
     'app_disable_comments_toggle' => 'Opmerkingen uitschakelen',
     'app_disable_comments_desc' => 'Schakel opmerkingen uit op alle pagina\'s in de applicatie. Bestaande opmerkingen worden niet getoond.',
@@ -73,7 +73,7 @@ return [
     'maint' => 'Onderhoud',
     'maint_image_cleanup' => 'Afbeeldingen opschonen',
     'maint_image_cleanup_desc' => "Scant pagina- en revisie inhoud om te controleren welke afbeeldingen en tekeningen momenteel worden gebruikt en welke afbeeldingen overbodig zijn. Zorg ervoor dat je een volledige database en afbeelding backup maakt voordat je dit uitvoert.",
-    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_delete_images_only_in_revisions' => 'Ook afbeeldingen die alleen in oude pagina revisies bestaan verwijderen',
     'maint_image_cleanup_run' => 'Opschonen uitvoeren',
     'maint_image_cleanup_warning' => ':count potentieel ongebruikte afbeeldingen gevonden. Weet u zeker dat u deze afbeeldingen wilt verwijderen?',
     'maint_image_cleanup_success' => ':count potentieel ongebruikte afbeeldingen gevonden en verwijderd!',
@@ -85,41 +85,41 @@ return [
     'maint_send_test_email_mail_subject' => 'Test E-mail',
     '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.',
-    'maint_recycle_bin_desc' => 'Deleted shelves, books, chapters & pages are sent to the recycle bin so they can be restored or permanently deleted. Older items in the recycle bin may be automatically removed after a while depending on system configuration.',
-    'maint_recycle_bin_open' => 'Open Recycle Bin',
+    'maint_recycle_bin_desc' => 'Verwijderde planken, boeken, hoofdstukken en pagina\'s worden naar de prullenbak gestuurd om ze te herstellen of definitief te verwijderen. Oudere items in de prullenbak kunnen automatisch worden verwijderd, afhankelijk van de systeemconfiguratie.',
+    'maint_recycle_bin_open' => 'Prullenbak openen',
 
     // Recycle Bin
-    'recycle_bin' => 'Recycle Bin',
-    'recycle_bin_desc' => 'Here you can restore items that have been deleted or choose to permanently remove them from the system. This list is unfiltered unlike similar activity lists in the system where permission filters are applied.',
-    'recycle_bin_deleted_item' => 'Deleted Item',
-    'recycle_bin_deleted_by' => 'Deleted By',
-    'recycle_bin_deleted_at' => 'Deletion Time',
-    'recycle_bin_permanently_delete' => 'Permanently Delete',
-    'recycle_bin_restore' => 'Restore',
-    'recycle_bin_contents_empty' => 'The recycle bin is currently empty',
-    'recycle_bin_empty' => 'Empty Recycle Bin',
-    'recycle_bin_empty_confirm' => 'This will permanently destroy all items in the recycle bin including content contained within each item. Are you sure you want to empty the recycle bin?',
-    'recycle_bin_destroy_confirm' => 'This action will permanently delete this item, along with any child elements listed below, from the system and you will not be able to restore this content. Are you sure you want to permanently delete this item?',
-    'recycle_bin_destroy_list' => 'Items to be Destroyed',
-    'recycle_bin_restore_list' => 'Items to be Restored',
-    'recycle_bin_restore_confirm' => 'This action will restore the deleted item, including any child elements, to their original location. If the original location has since been deleted, and is now in the recycle bin, the parent item will also need to be restored.',
-    'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
-    'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
-    'recycle_bin_restore_notification' => 'Restored :count total items from the recycle bin.',
+    'recycle_bin' => 'Prullenbak',
+    'recycle_bin_desc' => 'Hier kunt u items herstellen die zijn verwijderd of kiezen om ze permanent te verwijderen uit het systeem. Deze lijst is niet gefilterd, in tegenstelling tot vergelijkbare activiteitenlijsten in het systeem waar rechtenfilters worden toegepast.',
+    'recycle_bin_deleted_item' => 'Verwijderde Item',
+    'recycle_bin_deleted_by' => 'Verwijderd door',
+    'recycle_bin_deleted_at' => 'Verwijdert op',
+    'recycle_bin_permanently_delete' => 'Permanent verwijderen',
+    'recycle_bin_restore' => 'Herstellen',
+    'recycle_bin_contents_empty' => 'De prullenbak is momenteel leeg',
+    'recycle_bin_empty' => 'Prullenbak legen',
+    'recycle_bin_empty_confirm' => 'Dit zal permanent alle items in de prullenbak vernietigen, inclusief inhoud die in elk item zit. Weet u zeker dat u de prullenbak wilt legen?',
+    'recycle_bin_destroy_confirm' => 'Deze actie zal dit item permanent verwijderen, samen met alle onderliggende elementen hieronder vanuit het systeem en u kunt deze inhoud niet herstellen. Weet u zeker dat u dit item permanent wilt verwijderen?',
+    'recycle_bin_destroy_list' => 'Te vernietigen objecten',
+    'recycle_bin_restore_list' => 'Items te herstellen',
+    'recycle_bin_restore_confirm' => 'Deze actie herstelt het verwijderde item, inclusief alle onderliggende elementen, op hun oorspronkelijke locatie. Als de oorspronkelijke locatie sindsdien is verwijderd en zich nu in de prullenbak bevindt, zal ook het bovenliggende item moeten worden hersteld.',
+    'recycle_bin_restore_deleted_parent' => 'De bovenliggende map van dit item is ook verwijderd. Deze zal worden verwijderd totdat het bovenliggende item ook is hersteld.',
+    'recycle_bin_destroy_notification' => 'Verwijderde totaal :count items uit de prullenbak.',
+    'recycle_bin_restore_notification' => 'Herstelde totaal :count items uit de prullenbak.',
 
     // 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_related' => 'Related Item or Detail',
-    'audit_table_date' => 'Activity Date',
-    'audit_date_from' => 'Date Range From',
-    'audit_date_to' => 'Date Range To',
+    'audit_desc' => 'Dit auditlogboek toont een lijst met activiteiten die in het systeem zijn gedaan. Deze lijst is niet gefilterd, in tegenstelling tot vergelijkbare activiteitenlijsten in het systeem waar rechtenfilters worden toegepast.',
+    'audit_event_filter' => 'Gebeurtenis filter',
+    'audit_event_filter_no_filter' => 'Geen filter',
+    'audit_deleted_item' => 'Verwijderd Item',
+    'audit_deleted_item_name' => 'Naam: :name',
+    'audit_table_user' => 'Gebruiker',
+    'audit_table_event' => 'Gebeurtenis',
+    'audit_table_related' => 'Gerelateerd Item of Detail',
+    'audit_table_date' => 'Activiteit datum',
+    'audit_date_from' => 'Datum bereik vanaf',
+    'audit_date_to' => 'Datum bereik tot',
 
     // Role Settings
     'roles' => 'Rollen',
@@ -146,7 +146,7 @@ return [
     '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.',
+    'roles_system_warning' => 'Wees ervan bewust dat toegang tot een van de bovengenoemde drie machtigingen een gebruiker in staat kan stellen zijn eigen privileges of de privileges van anderen in het systeem te wijzigen. Wijs alleen rollen toe met deze machtigingen aan vertrouwde gebruikers.',
     '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',
@@ -162,7 +162,7 @@ return [
     'user_profile' => 'Gebruikersprofiel',
     'users_add_new' => 'Gebruiker toevoegen',
     'users_search' => 'Gebruiker zoeken',
-    'users_latest_activity' => 'Latest Activity',
+    'users_latest_activity' => 'Laatste activiteit',
     'users_details' => 'Gebruiker details',
     'users_details_desc' => 'Stel een weergavenaam en e-mailadres in voor deze gebruiker. Het e-mailadres zal worden gebruikt om in te loggen.',
     'users_details_desc_no_email' => 'Stel een weergavenaam in voor deze gebruiker zodat anderen deze kunnen herkennen.',
@@ -173,17 +173,17 @@ return [
     'users_send_invite_text' => 'U kunt ervoor kiezen om deze gebruiker een uitnodigingsmail te sturen waarmee hij zijn eigen wachtwoord kan instellen, anders kunt u zelf zijn wachtwoord instellen.',
     'users_send_invite_option' => 'Stuur gebruiker uitnodigings e-mail',
     'users_external_auth_id' => 'Externe authenticatie 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' => 'Dit is het ID dat gebruikt wordt om deze gebruiker te vergelijken met uw externe verificatiesysteem.',
     'users_password_warning' => 'Vul onderstaande formulier alleen in als je het wachtwoord wilt aanpassen:',
     'users_system_public' => 'De eigenschappen van deze gebruiker worden voor elke gastbezoeker gebruikt. Er kan niet mee ingelogd worden en wordt automatisch toegewezen.',
     'users_delete' => 'Verwijder gebruiker',
     'users_delete_named' => 'Verwijder gebruiker :userName',
     'users_delete_warning' => 'Dit zal de gebruiker \':userName\' volledig uit het systeem verwijderen.',
     'users_delete_confirm' => 'Weet je zeker dat je deze gebruiker wilt verwijderen?',
-    'users_migrate_ownership' => 'Migrate Ownership',
-    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
-    'users_none_selected' => 'No user selected',
-    'users_delete_success' => 'User successfully removed',
+    'users_migrate_ownership' => 'Draag eigendom over',
+    'users_migrate_ownership_desc' => 'Selecteer een gebruiker hier als u wilt dat een andere gebruiker de eigenaar wordt van alle items die momenteel eigendom zijn van deze gebruiker.',
+    'users_none_selected' => 'Geen gebruiker geselecteerd',
+    'users_delete_success' => 'Gebruiker succesvol verwijderd',
     'users_edit' => 'Bewerk Gebruiker',
     'users_edit_profile' => 'Bewerk Profiel',
     'users_edit_success' => 'Gebruiker succesvol bijgewerkt',
@@ -198,31 +198,31 @@ return [
     'users_social_connected' => ':socialAccount account is succesvol aan je profiel gekoppeld.',
     'users_social_disconnected' => ':socialAccount account is succesvol ontkoppeld van je profiel.',
     '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_none' => 'Er zijn geen API-tokens gemaakt voor deze gebruiker',
+    'users_api_tokens_create' => 'Token aanmaken',
+    'users_api_tokens_expires' => 'Verloopt',
+    'users_api_tokens_docs' => 'API Documentatie',
 
     // API Tokens
-    'user_api_token_create' => 'Create API Token',
+    'user_api_token_create' => 'API-token aanmaken',
     '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_name_desc' => 'Geef je token een leesbare naam als een toekomstige herinnering aan het beoogde doel.',
     '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_success' => 'API token successfully created',
-    'user_api_token_update_success' => 'API token successfully updated',
+    'user_api_token_expiry_desc' => 'Stel een datum in waarop deze token verloopt. Na deze datum zullen aanvragen die met deze token zijn ingediend niet langer werken. Als dit veld leeg blijft, wordt een vervaldatum van 100 jaar in de toekomst ingesteld.',
+    'user_api_token_create_secret_message' => 'Onmiddellijk na het aanmaken van dit token zal een "Token ID" en "Token Geheim" worden gegenereerd en weergegeven. Het geheim zal slechts één keer getoond worden. Kopieer de waarde dus eerst op een veilige plaats voordat u doorgaat.',
+    'user_api_token_create_success' => 'API token succesvol aangemaakt',
+    'user_api_token_update_success' => 'API token succesvol bijgewerkt',
     '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_id_desc' => 'Dit is een niet bewerkbaar systeem gegenereerde id voor dit token dat moet worden verstrekt in API-verzoeken.',
+    'user_api_token_secret' => 'Geheime token sleutel',
+    'user_api_token_secret_desc' => 'Dit is een systeem gegenereerd geheim voor dit token dat moet worden verstrekt in API verzoeken. Dit wordt maar één keer weergegeven, dus kopieër deze waarde naar een veilige plaats.',
+    'user_api_token_created' => 'Token gemaakt :timeAgo',
+    'user_api_token_updated' => 'Token bijgewerkt :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',
+    'user_api_token_delete_warning' => 'Dit zal de API-token met de naam \':tokenName\' volledig uit het systeem verwijderen.',
+    'user_api_token_delete_confirm' => 'Weet u zeker dat u deze API-token wilt verwijderen?',
+    'user_api_token_delete_success' => 'API-token succesvol verwijderd',
 
     //! If editing translations files directly please ignore this in all
     //! languages apart from en. Content will be auto-copied from en.
@@ -232,7 +232,7 @@ return [
         'ar' => 'العربية',
         'bg' => 'Bǎlgarski',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Catalaans',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index fa169105068943bc69914bf4f56c9df62f12f0e2..f0e99ad912fd373ad75cbc88203c5d4b82d8a452 100644 (file)
@@ -89,7 +89,7 @@ return [
     'required_without'     => ':attribute veld is verplicht wanneer :values niet ingesteld is.',
     'required_without_all' => ':attribute veld is verplicht wanneer geen van :values ingesteld zijn.',
     'same'                 => ':attribute en :other moeten overeenkomen.',
-    'safe_url'             => 'The provided link may not be safe.',
+    'safe_url'             => 'De opgegeven link is mogelijk niet veilig.',
     'size'                 => [
         'numeric' => ':attribute moet :size zijn.',
         'file'    => ':attribute moet :size kilobytes zijn.',
index 53f8486daa50b1b3e40db72074cb471f447a1adc..13e35e20611ede050a8f8f74251454e9b3a72a73 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'usunięto półkę',
     'bookshelf_delete_notification'    => 'Półka usunięta pomyślnie',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'skomentował',
     'permissions_update'          => 'zaktualizowane uprawnienia',
index e4c1439846026da5661ffc2dfda6e92c1495eb6a..716ee7ae5fccd02a9d5d2220aa1f02bd8669972b 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Usuń',
     'add' => 'Dodaj',
     'fullscreen' => 'Pełny ekran',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Opcje sortowania',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Ścieżka nawigacji',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobacz profil',
     'edit_profile' => 'Edytuj profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informacje',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Treść',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Jeśli masz problem z kliknięciem przycisku ":actionText", skopiuj i wklej poniższy adres URL w nowej karcie swojej przeglądarki:',
index 4fab043217c0df57ea1a8602d5526e7cac9c9131..9d172a87373d7c25cfe3306da0d4b21a577dc872 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Obrazki',
     'my_recent_drafts' => 'Moje ostatnie wersje robocze',
     'my_recently_viewed' => 'Moje ostatnio wyświetlane',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Nie przeglądałeś jeszcze żadnych stron',
     'no_pages_recently_created' => 'Nie utworzono ostatnio żadnych stron',
     'no_pages_recently_updated' => 'Nie zaktualizowano ostatnio żadnych stron',
index 5f5ec9db25150366d9c90c0f164814cb67a3386f..236c787a7f767d4ab1b71ba5c3742417e4180f50 100644 (file)
@@ -83,6 +83,9 @@ return [
     '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' => 'Jeśli spodziewałeś się, że ta strona istnieje, prawdopodobnie nie masz uprawnień do jej wyświetlenia.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Powrót do strony głównej',
     'error_occurred' => 'Wystąpił błąd',
     'app_down' => ':appName jest aktualnie wyłączona',
index 8e8a039482cd6064a1e3dbb80298f6fe6ad2b910..98130dafa564ea60c70368392d7298210248d564 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 8a5d15e8f91c5a00b14133a216b6c372447b2827..20194394f2d49a1e05f17278da7c476f69c80a3e 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'excluiu a prateleira',
     'bookshelf_delete_notification'    => 'Estante eliminada com sucesso',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'comentado a',
     'permissions_update'          => 'permissões atualizadas',
index dc63697910a963ff83a32ad5b39fef1d36487dd8..7dd2c9c63cd551726df4db87a9b26c1035ba6b81 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remover',
     'add' => 'Adicionar',
     'fullscreen' => 'Ecrã completo',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Opções de Ordenação',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Caminho',
 
     // Header
+    'header_menu_expand' => 'Expandir Menu de Cabeçalho',
     'profile_menu' => 'Menu de Perfil',
     'view_profile' => 'Visualizar Perfil',
     'edit_profile' => 'Editar Perfil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informações',
+    'tab_info_label' => 'Separador: Mostrar Informação Secundária',
     'tab_content' => 'Conteúdo',
+    'tab_content_label' => 'Separador: Mostrar Conteúdo Primário',
 
     // Email Content
     'email_action_help' => 'Se estiver com problemas ao carregar no botão ":actionText", copie e cole o URL abaixo no seu navegador:',
index 82ac03aa500fab6741814315c79d6ee2088903be..0b258f1448c0e9442bc5e5443aa6f88f484979f9 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Imagens',
     'my_recent_drafts' => 'Os Meus Rascunhos Recentes',
     'my_recently_viewed' => 'Visualizados Recentemente Por Mim',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Você não viu nenhuma página',
     'no_pages_recently_created' => 'Nenhuma página foi recentemente criada',
     'no_pages_recently_updated' => 'Nenhuma página foi recentemente atualizada',
index d1f3d8a5f4b928a651e846ee7cc1f1ee6f9f4cba..011b5b3a25e3bfbb30a1a4b7d6d1968d146af672 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Página Não Encontrada',
     'sorry_page_not_found' => 'Desculpe, a página que procura não foi encontrada.',
     'sorry_page_not_found_permission_warning' => 'Se esperava que esta página existisse, talvez não tenha permissão para visualizá-la.',
+    'image_not_found' => 'Imagem não encontrada',
+    'image_not_found_subtitle' => 'Desculpe, o arquivo de imagem que estava à procura não foi encontrado.',
+    'image_not_found_details' => 'Se estava à espera que a mesma existisse é possível que tenha sido eliminada.',
     'return_home' => 'Regressar à página inicial',
     'error_occurred' => 'Ocorreu um Erro',
     'app_down' => ':appName está fora do ar de momento',
index 3222d218dbd9b0e6754db139659c218547974843..c60b4dd1ba55cbc5d17411bcbb1892eb52e78ac9 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 33781417f755840ae0819234d2f065e2c03374e1..ad5a34398b98fb9cf72d02c6a8cde9e7b57fc072 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'excluiu a prateleira',
     'bookshelf_delete_notification'    => 'Prateleira excluída com sucesso',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'comentou em',
     'permissions_update'          => 'atualizou permissões',
index fda0028648ff687f639ebb430c9a755054fffa28..1757d955ab244b9ae4cad7237f7f996e423a2d27 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Remover',
     'add' => 'Adicionar',
     'fullscreen' => 'Tela cheia',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Opções de Ordenação',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Ordenação Crescente',
     'sort_descending' => 'Ordenação Decrescente',
     'sort_name' => 'Nome',
-    'sort_default' => 'Default',
+    'sort_default' => 'Padrão',
     'sort_created_at' => 'Data de Criação',
     'sort_updated_at' => 'Data de Atualização',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Caminho',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Menu de Perfil',
     'view_profile' => 'Visualizar Perfil',
     'edit_profile' => 'Editar Perfil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informações',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Conteúdo',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Se você estiver tendo problemas ao clicar o botão ":actionText", copie e cole a URL abaixo no seu navegador:',
index e67dfc8c37b7ba5819cb4ed5a5cc715203429953..a920bfd202c71f110d3347871237568927f79eab 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Imagens',
     'my_recent_drafts' => 'Meus Rascunhos Recentes',
     'my_recently_viewed' => 'Visualizados por mim Recentemente',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Você não visualizou nenhuma página',
     'no_pages_recently_created' => 'Nenhuma página criada recentemente',
     'no_pages_recently_updated' => 'Nenhuma página atualizada recentemente',
index c65d7f4969e14573eec31203e3f45a2dc955a0c8..d0e5c443904a186dbe1199c59d68e45696a3e4ad 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Página Não Encontrada',
     'sorry_page_not_found' => 'Desculpe, a página que você está procurando não pôde ser encontrada.',
     'sorry_page_not_found_permission_warning' => 'Se você esperava que esta página existisse, talvez você não tenha permissão para visualizá-la.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Retornar à página inicial',
     'error_occurred' => 'Ocorreu um Erro',
     'app_down' => ':appName está fora do ar no momento',
index 1eab1480378b1c88105ded8091880c6e9cb9b071..f06ffa835957ba6940bdfb9ac130b62b9ba55530 100644 (file)
@@ -39,9 +39,9 @@ return [
     'app_homepage_select' => 'Selecione uma página',
     'app_footer_links' => 'Links do Rodapé',
     'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
-    'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links_label' => 'Etiqueta do Link',
+    'app_footer_links_url' => 'URL do Link',
+    'app_footer_links_add' => 'Adicionar Link de Rodapé',
     'app_disable_comments' => 'Desativar Comentários',
     'app_disable_comments_toggle' => 'Desativar comentários',
     'app_disable_comments_desc' => 'Desativar comentários em todas as páginas no aplicativo.<br> Comentários existentes não serão exibidos.',
@@ -73,7 +73,7 @@ return [
     'maint' => 'Manutenção',
     'maint_image_cleanup' => 'Limpeza de Imagens',
     'maint_image_cleanup_desc' => "Examina páginas e revisa seus conteúdos para verificar quais imagens e desenhos estão atualmente em uso e quais são redundantes. Certifique-se de criar um backup completo do banco de dados e imagens antes de executar esta ação.",
-    'maint_delete_images_only_in_revisions' => 'Also delete images that only exist in old page revisions',
+    'maint_delete_images_only_in_revisions' => 'Também excluir imagens que existem apenas em revisões de página antigas',
     'maint_image_cleanup_run' => 'Executar Limpeza',
     'maint_image_cleanup_warning' => ':count imagens potencialmente não utilizadas foram encontradas. Tem certeza de que deseja excluir estas imagens?',
     'maint_image_cleanup_success' => ':count imagens potencialmente não utilizadas foram encontradas e excluídas!',
@@ -100,8 +100,8 @@ return [
     'recycle_bin_empty' => 'Esvaziar Lixeira',
     'recycle_bin_empty_confirm' => 'Isso irá destruir permanentemente todos os itens na lixeira inclusive o conteúdo de cada item. Tem certeza de que quer esvaziar a lixeira?',
     'recycle_bin_destroy_confirm' => 'Esta ação irá excluir permanentemente do sistema este item junto com todos os elementos filhos listados abaixo. Você não poderá restaurar esse conteúdo. Tem certeza de que deseja excluir permanentemente este item?',
-    'recycle_bin_destroy_list' => 'Items to be Destroyed',
-    'recycle_bin_restore_list' => 'Items to be Restored',
+    'recycle_bin_destroy_list' => 'Itens a serem Destruídos',
+    'recycle_bin_restore_list' => 'Itens a serem restaurados',
     'recycle_bin_restore_confirm' => 'Esta ação irá restaurar o item excluído, inclusive quaisquer elementos filhos, para seu local original. Se a localização original tiver, entretanto, sido eliminada e estiver agora na lixeira, o item pai também precisará ser restaurado.',
     'recycle_bin_restore_deleted_parent' => 'The parent of this item has also been deleted. These will remain deleted until that parent is also restored.',
     'recycle_bin_destroy_notification' => 'Deleted :count total items from the recycle bin.',
@@ -110,7 +110,7 @@ return [
     // Audit Log
     'audit' => 'Registro de auditoria',
     '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' => 'Filtro de Eventos',
     'audit_event_filter_no_filter' => 'Sem filtro',
     'audit_deleted_item' => 'Item excluído',
     'audit_deleted_item_name' => 'Nome: :name',
@@ -162,7 +162,7 @@ return [
     'user_profile' => 'Perfil do Usuário',
     'users_add_new' => 'Adicionar Novo Usuário',
     'users_search' => 'Pesquisar Usuários',
-    'users_latest_activity' => 'Latest Activity',
+    'users_latest_activity' => 'Última Atividade',
     'users_details' => 'Detalhes do Usuário',
     'users_details_desc' => 'Defina um nome de exibição e um endereço de e-mail para este usuário. O endereço de e-mail será usado para fazer login na aplicação.',
     'users_details_desc_no_email' => 'Defina um nome de exibição para este usuário para que outros usuários possam reconhecê-lo',
@@ -182,8 +182,8 @@ return [
     'users_delete_confirm' => 'Tem certeza que deseja excluir esse usuário?',
     'users_migrate_ownership' => 'Migrate Ownership',
     'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
-    'users_none_selected' => 'No user selected',
-    'users_delete_success' => 'User successfully removed',
+    'users_none_selected' => 'Nenhum usuário selecionado',
+    'users_delete_success' => 'Usuário removido com sucesso',
     'users_edit' => 'Editar Usuário',
     'users_edit_profile' => 'Editar Perfil',
     'users_edit_success' => 'Usuário atualizado com sucesso',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index af5a7af1d93fc7e8d8c4a91ccf593c5fc854fa22..4f9fb07e9777317c0d8978cd605b04a2d1970625 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'удалил полку',
     'bookshelf_delete_notification'    => 'Полка успешно удалена',
 
+    // Favourites
+    'favourite_add_notification' => '":name" добавлено в избранное',
+    'favourite_remove_notification' => ':name" удалено из избранного',
+
     // Other
     'commented_on'                => 'прокомментировал',
     'permissions_update'          => 'обновил разрешения',
index 5f72203f1a54297240a9e577fe87d0ad00e026e4..fce83880edb5185c10441ff879533d81be8f4be8 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Удалить',
     'add' => 'Добавить',
     'fullscreen' => 'На весь экран',
+    'favourite' => 'Избранное',
+    'unfavourite' => 'Убрать из избранного',
+    'next' => 'Следующая',
+    'previous' => 'Предыдущая',
 
     // Sort Options
     'sort_options' => 'Параметры сортировки',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Навигация',
 
     // Header
+    'header_menu_expand' => 'Развернуть меню заголовка',
     'profile_menu' => 'Меню профиля',
     'view_profile' => 'Посмотреть профиль',
     'edit_profile' => 'Редактировать профиль',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Информация',
+    'tab_info_label' => 'Вкладка: Показать вторичную информацию',
     'tab_content' => 'Содержание',
+    'tab_content_label' => 'Вкладка: Показать основной контент',
 
     // Email Content
     'email_action_help' => 'Если у вас возникли проблемы с нажатием кнопки \':actionText\', то скопируйте и вставьте указанный URL-адрес в свой браузер:',
index 4e1cca6ee0c0c172b22833bc52d7d5b58c7d326a..6d666b504088ae6143c424f454d70d434b72f05e 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Изображения',
     'my_recent_drafts' => 'Мои последние черновики',
     'my_recently_viewed' => 'Мои недавние просмотры',
+    'my_most_viewed_favourites' => 'Популярное избранное',
+    'my_favourites' => 'Мое избранное',
     'no_pages_viewed' => 'Вы не просматривали ни одной страницы',
     'no_pages_recently_created' => 'Нет недавно созданных страниц',
     'no_pages_recently_updated' => 'Нет недавно обновленных страниц',
index e8f537ecdaa9e4b22b5b3fd467959cba88d09f66..96c792e1dd828cd1b95c8536bc051a85c23f851a 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Страница не найдена',
     'sorry_page_not_found' => 'Извините, страница, которую вы искали, не найдена.',
     'sorry_page_not_found_permission_warning' => 'Если вы ожидали что страница существует, возможно у вас нет прав для её просмотра.',
+    'image_not_found' => 'Изображение не найдено',
+    'image_not_found_subtitle' => 'К сожалению, файл изображения, который вы искали, не найден.',
+    'image_not_found_details' => 'Возможно данное изображение было удалено.',
     'return_home' => 'вернуться на главную страницу',
     'error_occurred' => 'Произошла ошибка',
     'app_down' => ':appName в данный момент не доступно',
index 8eccbfdfefbef1acadae4cf7ab2da2a392a2c259..676fc54b2e91323c72dd6d8bbc7a0ab24a005a71 100755 (executable)
@@ -38,7 +38,7 @@ return [
     'app_homepage_desc' => 'Выберите страницу, которая будет отображаться на главной странице вместо стандартной. Права на страницы игнорируются для выбранных страниц.',
     'app_homepage_select' => 'Выберите страницу',
     'app_footer_links' => 'Ссылки в нижней части страницы',
-    'app_footer_links_desc' => 'Добавьте ссылки для отображения в нижнем колонтитуле сайта. Они будут отображаться внизу большинства страниц, включая те, которые не требуют входа. Вы можете использовать метку "trans::<key>" для использования системных переводов. Например: Использование "trans::common.privacy_policy" обеспечит перевод текста "Политика конфиденциальности" и "trans:common.terms_of_service" предоставит переведенный текст "Правила использования".',
+    'app_footer_links_desc' => 'Добавьте ссылки для отображения в нижнем колонтитуле сайта. Они будут отображаться в нижней части большинства страниц, включая те, которые не требуют входа. Вы можете использовать метку "trans::<key>" для использования системных переводов. Например: Использование "trans::common.privacy_policy" обеспечит перевод текста "Политика конфиденциальности" и "trans:common.terms_of_service" предоставит переведенный текст "Правила использования".',
     'app_footer_links_label' => 'Название ссылки',
     'app_footer_links_url' => 'Адрес ссылки',
     'app_footer_links_add' => 'Добавить ссылку',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index bbdec3cf7543c2c3d0206096397b41c9e8533336..15897e79107f6cfaad6223093401b1add2e4438c 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'odstránil(a) knižnicu',
     'bookshelf_delete_notification'    => 'Knižnica úspešne odstránená',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'komentoval(a)',
     'permissions_update'          => 'aktualizované oprávnenia',
index 7fc52cc14ee7a3bcfddfb5cc3d1ad38c631a6ec1..7a2051688d9ea94b12d7319ac4d06b4e3e174fa9 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Odstrániť',
     'add' => 'Pridať',
     'fullscreen' => 'Celá obrazovka',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Možnosti triedenia',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Breadcrumb',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Menu profilu',
     'view_profile' => 'Zobraziť profil',
     'edit_profile' => 'Upraviť profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informácie',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Obsah',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // 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 058d7f4c915a9bdeb73598f5fd64d450f1c6218f..7550255a9a004316f59bf095cd38bb5a49c60774 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Obrázky',
     'my_recent_drafts' => 'Moje nedávne koncepty',
     'my_recently_viewed' => 'Nedávno mnou zobrazené',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     '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é',
index c045d473d05e99e86971523018513dc50de667d8..e523110edea3a369f546aa8bba7ebf9961b76010 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Stránka nenájdená',
     'sorry_page_not_found' => 'Prepáčte, stránka ktorú hľadáte nebola nájdená.',
     'sorry_page_not_found_permission_warning' => 'If you expected this page to exist, you might not have permission to view it.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Vrátiť sa domov',
     'error_occurred' => 'Nastala chyba',
     'app_down' => ':appName je momentálne nedostupná',
index 60a1a5add6c06164ed83c89f1c900ffbbc33e7d1..eeb237981157bff33915a11940ecc935598ddd20 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 4ed1185def5c45acebd58adb81c9655d02eda531..91ec575e8e902ecc271b985f00a7faec9d9805d9 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'knjižna polica izbrisana',
     'bookshelf_delete_notification'    => 'Knjižna polica uspešno Izbrisana',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'komentar na',
     'permissions_update'          => 'pravice so posodobljene',
index d8a0d031df89e2ed258aa8d27effc4dc06215aaf..a34d91b3389164a375dd8a5dce530931516bbc24 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Odstrani',
     'add' => 'Dodaj',
     'fullscreen' => 'Celozaslonski način',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Možnosti razvrščanja',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Pot',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Meni profila',
     'view_profile' => 'Ogled profila',
     'edit_profile' => 'Uredi profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Informacije',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Vsebina',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'V kolikor imate težave s klikom na gumb ":actionText", kopirajte in prilepite spodnjo povezavo v vaš brskalnik:',
index c5ade055f72cc342ed6f8469b4b5d29bac80de70..699b7c4700a131a6208b71b5add197325bc7ac3e 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Slike',
     'my_recent_drafts' => 'Moji nedavni osnutki',
     'my_recently_viewed' => 'Nedavno prikazano',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Niste si ogledali še nobene strani',
     'no_pages_recently_created' => 'Nedavno ni bila ustvarjena nobena stran',
     'no_pages_recently_updated' => 'Nedavno ni bila posodobljena nobena stran',
index e291222c3f6be5f22cdbcbb61908c492d4619327..b0e253be0e8c28f68a25ec45cb00199ddba61090 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Strani ni mogoče najti',
     'sorry_page_not_found' => 'Oprostite, strani ki jo iščete, ni mogoče najti.',
     'sorry_page_not_found_permission_warning' => 'Če pričakujete, da ta stran obstaja, mogoče nimate pravic ogleda zanjo.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Vrni se domov',
     'error_occurred' => 'Prišlo je do napake',
     'app_down' => ':appName trenutno ni dosegljiva',
index 8373c8cebef6fce4d0f480c93a92e6dfbb7530e9..dc03828681fd7ee63cc6d0eb662ac0f835208e72 100644 (file)
@@ -242,6 +242,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index e3fa051555f92bd5f24edf72342f110d0b385f84..30c157a66d1dd53d244ca1f6d435f06ab3972cfe 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'tog bort hyllan',
     'bookshelf_delete_notification'    => 'Hyllan har tagits bort',
 
+    // Favourites
+    'favourite_add_notification' => '":name" har lagts till i dina favoriter',
+    'favourite_remove_notification' => '":name" har tagits bort från dina favoriter',
+
     // Other
     'commented_on'                => 'kommenterade',
     'permissions_update'          => 'uppdaterade behörigheter',
index e90b70ccc1964825398327022f831f6a8f99e23f..1482eeaab4a1a2d554d89d76b6bb8a319ed145b5 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Radera',
     'add' => 'Lägg till',
     'fullscreen' => 'Helskärm',
+    'favourite' => 'Favorit',
+    'unfavourite' => 'Ta bort favorit',
+    'next' => 'Nästa',
+    'previous' => 'Föregående',
 
     // Sort Options
     'sort_options' => 'Sorteringsalternativ',
@@ -47,7 +51,7 @@ return [
     'sort_ascending' => 'Sortera stigande',
     'sort_descending' => 'Sortera fallande',
     'sort_name' => 'Namn',
-    'sort_default' => 'Default',
+    'sort_default' => 'Standard',
     'sort_created_at' => 'Skapad',
     'sort_updated_at' => 'Uppdaterad',
 
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Brödsmula',
 
     // Header
+    'header_menu_expand' => 'Expandera sidhuvudsmenyn',
     'profile_menu' => 'Profilmeny',
     'view_profile' => 'Visa profil',
     'edit_profile' => 'Redigera profil',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Information',
+    'tab_info_label' => 'Flik: Visa sekundär information',
     'tab_content' => 'Innehåll',
+    'tab_content_label' => 'Flik: Visa primärt innehåll',
 
     // Email Content
     'email_action_help' => 'Om du har problem, klicka på knappen ":actionText", och kopiera och klistra in den här adressen i din webbläsare:',
@@ -81,6 +88,6 @@ return [
 
     // Footer Link Options
     // Not directly used but available for convenience to users.
-    'privacy_policy' => 'Privacy Policy',
-    'terms_of_service' => 'Terms of Service',
+    'privacy_policy' => 'Integritetspolicy',
+    'terms_of_service' => 'Användarvillkor',
 ];
index a0df3b608745fcdc4c5a13b26b41f8e2d3a0eef6..ce0dfddf2dc8150656f35b294d42326303c6d0f4 100644 (file)
@@ -22,11 +22,13 @@ return [
     'meta_created_name' => 'Skapad :timeLength av :user',
     'meta_updated' => 'Uppdaterad :timeLength',
     'meta_updated_name' => 'Uppdaterad :timeLength av :user',
-    'meta_owned_name' => 'Owned by :user',
+    'meta_owned_name' => 'Ägs av :user',
     'entity_select' => 'Välj enhet',
     'images' => 'Bilder',
     'my_recent_drafts' => 'Mina nyaste utkast',
     'my_recently_viewed' => 'Mina senast visade sidor',
+    'my_most_viewed_favourites' => 'Mina mest visade favoriter',
+    'my_favourites' => 'Mina favoriter',
     'no_pages_viewed' => 'Du har inte visat några sidor',
     'no_pages_recently_created' => 'Inga sidor har skapats nyligen',
     'no_pages_recently_updated' => 'Inga sidor har uppdaterats nyligen',
@@ -40,7 +42,7 @@ return [
     'permissions_intro' => 'Dessa rättigheter kommer att överskrida eventuella rollbaserade rättigheter.',
     'permissions_enable' => 'Aktivera anpassade rättigheter',
     'permissions_save' => 'Spara rättigheter',
-    'permissions_owner' => 'Owner',
+    'permissions_owner' => 'Ägare',
 
     // Search
     'search_results' => 'Sökresultat',
@@ -60,7 +62,7 @@ return [
     'search_permissions_set' => 'Har anpassade rättigheter',
     'search_created_by_me' => 'Skapade av mig',
     'search_updated_by_me' => 'Uppdaterade av mig',
-    'search_owned_by_me' => 'Owned by me',
+    'search_owned_by_me' => 'Ägs av mig',
     'search_date_options' => 'Datumalternativ',
     'search_updated_before' => 'Uppdaterade före',
     'search_updated_after' => 'Uppdaterade efter',
@@ -211,7 +213,7 @@ return [
     'pages_revisions' => 'Sidrevisioner',
     'pages_revisions_named' => 'Sidrevisioner för :pageName',
     'pages_revision_named' => 'Sidrevision för :pageName',
-    'pages_revision_restored_from' => 'Restored from #:id; :summary',
+    'pages_revision_restored_from' => 'Återställd från #:id; :summary',
     'pages_revisions_created_by' => 'Skapad av',
     'pages_revisions_date' => 'Revisionsdatum',
     'pages_revisions_number' => '#',
index dbc3f37f237021350066bfe1fc36a29e4be4566e..b8216a42fa5d71c4de13befff703dff85ec15926 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Sidan hittades inte',
     'sorry_page_not_found' => 'Tyvärr gick det inte att hitta sidan du söker.',
     'sorry_page_not_found_permission_warning' => 'Om du förväntade dig att denna sida skulle existera, kanske du inte har behörighet att se den.',
+    'image_not_found' => 'Bilden hittades inte',
+    'image_not_found_subtitle' => 'Tyvärr gick det inte att hitta bilden du letade efter.',
+    'image_not_found_details' => 'Om du förväntade dig att den här bilden skulle finnas kan den ha tagits bort.',
     'return_home' => 'Återvänd till startsidan',
     'error_occurred' => 'Ett fel inträffade',
     'app_down' => ':appName är nere just nu',
index 07b1b5d4a19f02b4321631aa240258e4201c9986..d5044efbd36b350b0f0d81c0d3057f136930c151 100644 (file)
@@ -37,11 +37,11 @@ return [
     'app_homepage' => 'Startsida',
     'app_homepage_desc' => 'Välj en sida att använda som startsida istället för standardvyn. Den valda sidans rättigheter kommer att ignoreras.',
     'app_homepage_select' => 'Välj en sida',
-    'app_footer_links' => 'Footer Links',
-    'app_footer_links_desc' => 'Add links to show within the site footer. These will be displayed at the bottom of most pages, including those that do not require login. You can use a label of "trans::<key>" to use system-defined translations. For example: Using "trans::common.privacy_policy" will provide the translated text "Privacy Policy" and "trans::common.terms_of_service" will provide the translated text "Terms of Service".',
-    'app_footer_links_label' => 'Link Label',
-    'app_footer_links_url' => 'Link URL',
-    'app_footer_links_add' => 'Add Footer Link',
+    'app_footer_links' => 'Sidfotslänkar',
+    'app_footer_links_desc' => 'Lägg till länkar som visas i sidfoten. Dessa kommer att visas längst ner på de flesta sidor, inklusive de som inte kräver inloggning. Du kan använda en etikett av "trans::<key>" för att använda systemdefinierade översättningar. Exempelvis översätts "trans::common.privacy_policy" till "Integritetspolicy" och "trans::common.terms_of_service" till "Användarvillkor".',
+    'app_footer_links_label' => 'Länketikett',
+    'app_footer_links_url' => 'Länk URL',
+    'app_footer_links_add' => 'Lägg till sidfotslänk',
     'app_disable_comments' => 'Inaktivera kommentarer',
     'app_disable_comments_toggle' => 'Inaktivera kommentarer',
     'app_disable_comments_desc' => 'Inaktivera kommentarer på alla sidor i applikationen. Befintliga kommentarer visas inte.',
@@ -180,10 +180,10 @@ return [
     'users_delete_named' => 'Ta bort användaren :userName',
     'users_delete_warning' => 'Detta kommer att ta bort användaren \':userName\' från systemet helt och hållet.',
     'users_delete_confirm' => 'Är du säker på att du vill ta bort användaren?',
-    'users_migrate_ownership' => 'Migrate Ownership',
-    'users_migrate_ownership_desc' => 'Select a user here if you want another user to become the owner of all items currently owned by this user.',
-    'users_none_selected' => 'No user selected',
-    'users_delete_success' => 'User successfully removed',
+    'users_migrate_ownership' => 'Överför ägarskap',
+    'users_migrate_ownership_desc' => 'Välj en användare här om du vill att en annan användare ska bli ägare till alla objekt som för närvarande ägs av denna användare.',
+    'users_none_selected' => 'Ingen användare vald',
+    'users_delete_success' => 'Användaren har tagits bort',
     'users_edit' => 'Redigera användare',
     'users_edit_profile' => 'Redigera profil',
     'users_edit_success' => 'Användaren har uppdaterats',
@@ -232,7 +232,7 @@ return [
         'ar' => 'العربية',
         'bg' => 'Bǎlgarski',
         'bs' => 'Bosanski',
-        'ca' => 'Català',
+        'ca' => 'Katalanska',
         'cs' => 'Česky',
         'da' => 'Danska',
         'de' => 'Deutsch (Sie)',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 67f9653f432c1975a7164bf509cce8d6b1611f0f..9bcd1891f14a925a4dd8726cfd230968dbbedf22 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'kitaplığı sildi',
     'bookshelf_delete_notification'    => 'Kitaplık Başarıyla Silindi',
 
+    // Favourites
+    'favourite_add_notification' => '":name" favorilerinize eklendi',
+    'favourite_remove_notification' => '":name" favorilerinizden çıkarıldı',
+
     // Other
     'commented_on'                => 'yorum yaptı',
     'permissions_update'          => 'güncellenmiş izinler',
index 5931434000d95a2348c657620d981a60abe2be42..50ce70e619d199a980db2c6ca706e59994d3d0b5 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Kaldır',
     'add' => 'Ekle',
     'fullscreen' => 'Tam Ekran',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Sıralama Seçenekleri',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Gezinti Menüsü',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Profil Menüsü',
     'view_profile' => 'Profili Görüntüle',
     'edit_profile' => 'Profili Düzenle',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Bilgi',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'İçerik',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => '":actionText" butonuna tıklamada sorun yaşıyorsanız, aşağıda bulunan bağlantıyı kopyalayıp tarayıcınıza yapıştırın:',
index 64a0ee35268ed2df9ca355ea59eccbf800515c02..e48277a4bbdfc4d9bbc58691a84964fc4e1b2bbf 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Görseller',
     'my_recent_drafts' => 'Son Taslaklarım',
     'my_recently_viewed' => 'Son Görüntülediklerim',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Herhangi bir sayfa görüntülemediniz',
     'no_pages_recently_created' => 'Yakın zamanda bir sayfa oluşturulmadı',
     'no_pages_recently_updated' => 'Yakın zamanda bir sayfa güncellenmedi',
index d3a24622c2c8eb73bba91c3c5a4a97155aa27154..5048b079cf6045597d15d90b59347fa06d1fcce2 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Sayfa Bulunamadı',
     'sorry_page_not_found' => 'Üzgünüz, aradığınız sayfa bulunamıyor.',
     'sorry_page_not_found_permission_warning' => 'Bu sayfanın var olduğunu düşünüyorsanız, görüntüleme iznine sahip olmayabilirsiniz.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Ana sayfaya dön',
     'error_occurred' => 'Bir Hata Oluştu',
     'app_down' => ':appName şu anda erişilemez durumda',
index 8ba1192c75094dc0deda77923ee19b8d2a687d8b..4b015c04244331866b7e1f0370feea0e1649e51d 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'İbranice',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index f16f6fe3edc0fd3b37941cc77d79a0aeaa5e795c..918eb1a41f6968a28d02f80093fb072eba739f00 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'видалив книжкову полицю',
     'bookshelf_delete_notification'    => 'Книжкову полицю успішно видалено',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'прокоментував',
     'permissions_update'          => 'оновив дозволи',
index bbaa64706363dba3033421aac51c96547c52911c..62342a723aa882aded4d5ec598e5ea7a479146f0 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Видалити',
     'add' => 'Додати',
     'fullscreen' => 'На весь екран',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Параметри сортування',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Навігація',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Меню профілю',
     'view_profile' => 'Переглянути профіль',
     'edit_profile' => 'Редагувати профіль',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Інфо',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Вміст',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Якщо у вас виникають проблеми при натисканні кнопки ":actionText", скопіюйте та вставте URL у свій веб-браузер:',
index 86f044102ac39f884f6877ea4c2c044148b2f4af..f41d97bb00fb724b426caf93b93440141d13545b 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Зображення',
     'my_recent_drafts' => 'Мої останні чернетки',
     'my_recently_viewed' => 'Мої недавні перегляди',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Ви не переглядали жодної сторінки',
     'no_pages_recently_created' => 'Не було створено жодної сторінки',
     'no_pages_recently_updated' => 'Немає недавно оновлених сторінок',
index eb20bae74bf57e921caf9cbce25e2448d228e049..c7d2545f995565c6935b0b8325629401422180b5 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Сторінку не знайдено',
     'sorry_page_not_found' => 'Вибачте, сторінку, яку ви шукали, не знайдено.',
     'sorry_page_not_found_permission_warning' => 'Якщо ви очікували що ця сторінки існує – можливо у вас немає дозволу на її перегляд.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Повернутися на головну',
     'error_occurred' => 'Виникла помилка',
     'app_down' => ':appName зараз недоступний',
index b7988ba0922ea59365108260a50c2744531f1cb8..fdcd67034f4f9a69316a678cfb7c34470697f443 100644 (file)
@@ -232,7 +232,7 @@ return [
         'ar' => 'العربية',
         'bg' => 'Bǎlgarski',
         'bs' => 'Bosanski',
-        'ca' => 'Каталаньський',
+        'ca' => 'Català',
         'cs' => 'Česky',
         'da' => 'Dansk',
         'de' => 'Deutsch (Sie)',
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 85a850e274bcc4bb1e9d5c868ccacac7658bc2ac..10d24a16b4b9638635d24b2a3b3714c6e3342ea1 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => 'đã xóa giá sách',
     'bookshelf_delete_notification'    => 'Giá sách đã được xóa thành công',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => 'đã bình luận về',
     'permissions_update'          => 'các quyền đã được cập nhật',
index 069a9a3a6ebc4639125974188a56850532c92430..6e779da242d855f40bcac9b219b7d898ddb98f8f 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => 'Xóa bỏ',
     'add' => 'Thêm',
     'fullscreen' => 'Toàn màn hình',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => 'Tùy Chọn Sắp Xếp',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => 'Đường dẫn liên kết',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => 'Menu Hồ sơ',
     'view_profile' => 'Xem Hồ sơ',
     'edit_profile' => 'Sửa Hồ sơ',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => 'Thông tin',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => 'Nội dung',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => 'Nếu bạn đang có vấn đề trong việc bấm nút ":actionText", sao chép và dán địa chỉ URL dưới đây vào trình duyệt web:',
index e72f696488f49171af2ac5e9709ab94d9f094bbf..cfea0956e97e0e26f629c67009ae6630552aa6b9 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => 'Ảnh',
     'my_recent_drafts' => 'Bản nháp gần đây của tôi',
     'my_recently_viewed' => 'Xem gần đây',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => 'Bạn chưa xem bất cứ trang nào',
     'no_pages_recently_created' => 'Không có trang nào được tạo gần đây',
     'no_pages_recently_updated' => 'Không có trang nào được cập nhật gần đây',
index 6410fb97092748f0b91064b4b91d49d5d270e714..0c4a6fa8f5dbd2fc396e8916ff74101251a531f2 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => 'Không Tìm Thấy Trang',
     'sorry_page_not_found' => 'Xin lỗi, Không tìm thấy trang bạn đang tìm kiếm.',
     'sorry_page_not_found_permission_warning' => 'Nếu trang bạn tìm kiếm tồn tại, có thể bạn đang không có quyền truy cập.',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => 'Quay lại trang chủ',
     'error_occurred' => 'Đã xảy ra lỗi',
     'app_down' => ':appName hiện đang ngoại tuyến',
index 6f282a568c4b28ffbb865b3862ab99b3c17af9dd..a105e82c7eac111e106f3322f1842f0d7a080fc7 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 717c7dfdf98a7bbd4b292201a4274bf49cd746ea..cc453abf67a4ee42cfa256b47d6f6bd784dbb74a 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => '删除了书架',
     'bookshelf_delete_notification'    => '书架已成功删除',
 
+    // Favourites
+    'favourite_add_notification' => '":name" 已添加到你的收藏',
+    'favourite_remove_notification' => '":name" 已从你的收藏中删除',
+
     // Other
     'commented_on'                => '评论',
     'permissions_update'          => '权限已更新',
index a7f8017cb68e81bf4b6b2a05c0c85414a1580e6f..b883e0b6c4535cfe9a5d495780f0f7b1077e5f6c 100644 (file)
@@ -13,10 +13,10 @@ return [
     'sign_up' => '注册',
     'log_in' => '登录',
     'log_in_with' => '以:socialDriver登录',
-    'sign_up_with' => '注册:socialDriver',
+    'sign_up_with' => '通过 :socialDriver 账号登录',
     'logout' => '注销',
 
-    'name' => 'å§\93å\90\8d',
+    'name' => 'å\90\8dç§°',
     'username' => '用户名',
     'email' => 'Email地址',
     'password' => '密码',
@@ -27,10 +27,10 @@ return [
     'ldap_email_hint' => '请输入用于此帐户的电子邮件。',
     'create_account' => '创建账户',
     'already_have_account' => '您已经有账号?',
-    'dont_have_account' => 'æ\82¨è¿\98没注å\86\8c?',
+    'dont_have_account' => 'æ\82¨è¿\98没æ\9c\89è´¦å\8f·å\90\97?',
     'social_login' => 'SNS登录',
-    'social_registration' => 'SNS注册',
-    'social_registration_text' => '其他服务注册/登录。',
+    'social_registration' => '使用社交网站账号注册',
+    'social_registration_text' => '使用其他服务注册并登录。',
 
     'register_thanks' => '注册完成!',
     'register_confirm' => '请点击查收您的Email,并点击确认。',
index 8fa2f0ffa2fb3fc8d01080c02c85e9e358d160bf..c8a0eef075eb36404767698c2eb2f449f21c12cc 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => '删除',
     'add' => '添加',
     'fullscreen' => '全屏',
+    'favourite' => '收藏',
+    'unfavourite' => '不喜欢',
+    'next' => '下一页',
+    'previous' => '上一页',
 
     // Sort Options
     'sort_options' => '排序选项',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => '面包屑导航',
 
     // Header
+    'header_menu_expand' => '展开标头菜单',
     'profile_menu' => '个人资料',
     'view_profile' => '查看资料',
     'edit_profile' => '编辑资料',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => '信息',
+    'tab_info_label' => '标签:显示次要信息',
     'tab_content' => '内容',
+    'tab_content_label' => '标签:显示主要内容',
 
     // Email Content
     'email_action_help' => '如果您无法点击“:actionText”按钮,请将下面的网址复制到您的浏览器中打开:',
index 09564a70a09a36862fd8673d30856ddea0d06540..494ac717f48fbe1138b450f72adf182dc53d82ea 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => '图片',
     'my_recent_drafts' => '我最近的草稿',
     'my_recently_viewed' => '我最近看过',
+    'my_most_viewed_favourites' => '我浏览最多的收藏',
+    'my_favourites' => '我的收藏',
     'no_pages_viewed' => '您尚未查看任何页面',
     'no_pages_recently_created' => '最近没有页面被创建',
     'no_pages_recently_updated' => '最近没有页面被更新',
index b41e21ac3e83b9858edece28d99428dfa581f2b8..7ad049ed40bdbfc9decfb985f30b25d6922646e1 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => '无法找到页面',
     'sorry_page_not_found' => '对不起,无法找到您想访问的页面。',
     'sorry_page_not_found_permission_warning' => '您可能没有查看权限。',
+    'image_not_found' => '未找到图片',
+    'image_not_found_subtitle' => '对不起,无法找到您想访问的图片。',
+    'image_not_found_details' => '原本放在这里的图片已被删除。',
     'return_home' => '返回主页',
     'error_occurred' => '出现错误',
     'app_down' => ':appName现在正在关闭',
index ed222d7f492a9ac2d7ca82573f34f39a1484f3c8..b6152425f7569e7900d3815086a9bc7ee87b7e78 100755 (executable)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => 'עברית',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 0c86665b03df03f90b79e0fc65f20f95fc2770c7..6d07a176a6b605bf457772baeea2a2a6fcfb532f 100644 (file)
@@ -43,6 +43,10 @@ return [
     'bookshelf_delete'                 => '已刪除書架',
     'bookshelf_delete_notification'    => '書架已刪除成功',
 
+    // Favourites
+    'favourite_add_notification' => '":name" has been added to your favourites',
+    'favourite_remove_notification' => '":name" has been removed from your favourites',
+
     // Other
     'commented_on'                => '評論',
     'permissions_update'          => '更新權限',
index e7d3b39081fc7310c9085f289081f121388b61e4..6d1d15f3e308aa58f3b4ac04432035db531df45f 100644 (file)
@@ -40,6 +40,10 @@ return [
     'remove' => '移除',
     'add' => '新增',
     'fullscreen' => '全螢幕',
+    'favourite' => 'Favourite',
+    'unfavourite' => 'Unfavourite',
+    'next' => 'Next',
+    'previous' => 'Previous',
 
     // Sort Options
     'sort_options' => '排序選項',
@@ -65,6 +69,7 @@ return [
     'breadcrumb' => '頁面路徑',
 
     // Header
+    'header_menu_expand' => 'Expand Header Menu',
     'profile_menu' => '個人資料選單',
     'view_profile' => '檢視個人資料',
     'edit_profile' => '編輯個人資料',
@@ -73,7 +78,9 @@ return [
 
     // Layout tabs
     'tab_info' => '資訊',
+    'tab_info_label' => 'Tab: Show Secondary Information',
     'tab_content' => '內容',
+    'tab_content_label' => 'Tab: Show Primary Content',
 
     // Email Content
     'email_action_help' => '如果您無法點擊 ":actionText" 按鈕,請將下方的網址複製並貼上到您的網路瀏覽器中:',
index 564cd3891d2a59bbcfad668999dce98aa0ca805c..32e3792be5858d7d16b4f3ceea86d2d76a7f169a 100644 (file)
@@ -27,6 +27,8 @@ return [
     'images' => '圖片',
     'my_recent_drafts' => '我最近的草稿',
     'my_recently_viewed' => '我最近檢視',
+    'my_most_viewed_favourites' => 'My Most Viewed Favourites',
+    'my_favourites' => 'My Favourites',
     'no_pages_viewed' => '您尚未看過任何頁面',
     'no_pages_recently_created' => '最近未建立任何頁面',
     'no_pages_recently_updated' => '最近沒有頁面被更新',
index cdb346e2c4adf3b943e991bd3e8057cd87932948..0435fc8ad4c9b8f269bbf4e879913865ca48861d 100644 (file)
@@ -83,6 +83,9 @@ return [
     '404_page_not_found' => '找不到頁面',
     'sorry_page_not_found' => '抱歉,找不到您在尋找的頁面。',
     'sorry_page_not_found_permission_warning' => '如果您確認這個頁面存在,則代表可能沒有查看它的權限。',
+    'image_not_found' => 'Image Not Found',
+    'image_not_found_subtitle' => 'Sorry, The image file you were looking for could not be found.',
+    'image_not_found_details' => 'If you expected this image to exist it might have been deleted.',
     'return_home' => '回到首頁',
     'error_occurred' => '發生錯誤',
     'app_down' => ':appName 離線中',
index 278049052290778ffdd3fe90126d6d4592e173a2..77c1d8b83688c15a5f67ce1871a3288310e1a627 100644 (file)
@@ -241,6 +241,7 @@ return [
         'es_AR' => 'Español Argentina',
         'fr' => 'Français',
         'he' => '希伯來語',
+        'hr' => 'Hrvatski',
         'hu' => 'Magyar',
         'id' => 'Bahasa Indonesia',
         'it' => 'Italian',
index 75adf12aacde34a99b795c7278320f6ebdfee62b..f9c2061547fbdf5daf598d6ca75b7973d72fabfc 100644 (file)
   padding: $-m $-xxl;
   margin-inline-start: auto;
   margin-inline-end: auto;
-  margin-bottom: $-xl;
+  margin-bottom: $-l;
   overflow: initial;
   min-height: 60vh;
   &.auto-height {
   }
 }
 
+.outline-hover {
+  border: 1px solid transparent !important;
+  &:hover {
+    border: 1px solid rgba(0, 0, 0, 0.1) !important;
+  }
+}
+
+.fade-in-when-active {
+  opacity: 0.6;
+  transition: opacity ease-in-out 120ms;
+  &:hover, &:focus-within {
+    opacity: 1;
+  }
+}
+
 /**
  * Tags
  */
   margin-bottom: $-xs;
   margin-inline-end: $-xs;
   border-radius: 4px;
-  border: 1px solid #CCC;
+  border: 1px solid;
   overflow: hidden;
   font-size: 0.85em;
-  a, a:hover, a:active {
+  @include lightDark(border-color, #CCC, #666);
+  a, span, a:hover, a:active {
     padding: 4px 8px;
-    @include lightDark(color, #777, #999);
+    @include lightDark(color, rgba(0, 0, 0, 0.6), rgba(255, 255, 255, 0.8));
     transition: background-color ease-in-out 80ms;
     text-decoration: none;
   }
     @include lightDark(background-color, rgba(255, 255, 255, 0.7), rgba(255, 255, 255, 0.3));
   }
   svg {
-    fill: #888;
+    @include lightDark(fill, rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5));
   }
   .tag-value {
-    border-inline-start: 1px solid #DDD;
+    border-inline-start: 1px solid;
+    @include lightDark(border-color, #DDD, #666);
     @include lightDark(background-color, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.2))
   }
 }
index bd76090523ca70a8ce24cba5ec5037ed7f5b74ae..850443d9a7c02a500832990eaa0b9d4e7a7ca8d5 100644 (file)
@@ -117,6 +117,7 @@ button {
   align-items: center;
   padding: $-s $-m;
   padding-bottom: ($-s - 2px);
+  width: 100%;
   svg {
     display: inline-block;
     width: 24px;
index ad630469438f770dcb6c95c204801f70475274ab..ab562006fd6ba488004c7833634c6f12e6b6ad46 100644 (file)
@@ -190,7 +190,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
   float: left;
   margin: 0;
   cursor: pointer;
-  width: (100%/6);
+  width: math.div(100%, 6);
   height: auto;
   @include lightDark(border-color, #ddd, #000);
   box-shadow: 0 0 0 0 rgba(0, 0, 0, 0);
@@ -219,7 +219,7 @@ body.flexbox-support #entity-selector-wrap .popup-body .form-group {
     }
   }
   @include smaller-than($xl) {
-    width: (100%/4);
+    width: math.div(100%, 4);
   }
   @include smaller-than($m) {
     .image-meta {
index 11ea1cc7f89bc6b29ff52001613b10e568f868e0..953d1d06054d23730b9c104a0a833980ce4f6e9b 100644 (file)
@@ -187,7 +187,7 @@ table.form-table {
   max-width: 100%;
   td {
     overflow: hidden;
-    padding: $-xxs/2 0;
+    padding: math.div($-xxs, 2) 0;
   }
 }
 
@@ -196,6 +196,16 @@ input[type="color"], input[type="password"], select, textarea {
   @extend .input-base;
 }
 
+select {
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  appearance: none;
+  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='100' height='100' fill='%23666666'><polygon points='0,0 100,0 50,50'/></svg>");
+  background-size: 12px;
+  background-position: calc(100% - 20px) 70%;
+  background-repeat: no-repeat;
+}
+
 input[type=date] {
   width: 190px;
 }
index f371e0410bfdf3f3aa2d58e5d8ec56a31d72e4d4..1a7015078e3891f44fa3511e0b88985073ce7f96 100644 (file)
@@ -191,6 +191,11 @@ header .search-box {
       @include lightDark(color, #000, #fff);
       text-decoration: none;
     }
+    &:focus {
+      @include lightDark(background-color, #eee, #333);
+      outline-color: var(--color-primary);
+      color: var(--color-primary);
+    }
   }
   header .dropdown-container {
     display: block;
@@ -216,17 +221,22 @@ header .search-box {
   z-index: 5;
   background-color: #FFF;
   border-bottom: 1px solid #DDD;
+  @include lightDark(border-bottom-color, #DDD, #333);
   box-shadow: $bs-card;
 }
 .tri-layout-mobile-tab {
   text-align: center;
   border-bottom: 3px solid #BBB;
   cursor: pointer;
+  margin: 0;
+  @include lightDark(background-color, #FFF, #222);
+  @include lightDark(border-bottom-color, #BBB, #333);
   &:first-child {
     border-inline-end: 1px solid #DDD;
+    @include lightDark(border-inline-end-color, #DDD, #000);
   }
-  &.active {
-    border-bottom-color: currentColor;
+  &[aria-selected="true"] {
+    border-bottom-color: currentColor !important;
   }
 }
 
index 60205eaaacc3f42088800874b6760f963bc9ae26..516d7d612e590d7a6ec516a689cdd102ff6606c7 100644 (file)
@@ -157,6 +157,9 @@ body.flexbox {
 .justify-center {
   justify-content: center;
 }
+.justify-space-between {
+  justify-content: space-between;
+}
 .items-center {
   align-items: center;
 }
@@ -209,6 +212,13 @@ body.flexbox {
   }
 }
 
+/**
+ * Border radiuses
+ */
+.rounded {
+  border-radius: 4px;
+}
+
 /**
  * Inline content columns
  */
@@ -363,4 +373,4 @@ body.flexbox {
     margin-inline-start: 0;
     margin-inline-end: 0;
   }
-}
+}
\ No newline at end of file
index d6ea66350ebecf15fd0a59b95528e713923a990c..436c7e5333c359dd6cd1e2cb959d9013ca8a154c 100644 (file)
@@ -441,12 +441,8 @@ ul.pagination {
     background-color: rgba(0, 0, 0, 0.1);
     border-radius: 4px;
   }
-  &.outline-hover {
-    border: 1px solid transparent;
-  }
   &.outline-hover:hover {
     background-color: transparent;
-    border-color: rgba(0, 0, 0, 0.1);
   }
   &:focus {
     @include lightDark(background-color, #eee, #222);
@@ -550,6 +546,17 @@ ul.pagination {
   }
 }
 
+.entity-item-tags {
+  font-size: .75rem;
+  opacity: 1;
+  .primary-background-light {
+    background: transparent;
+  }
+  .tag-name {
+    background-color: rgba(0, 0, 0, 0.05);
+  }
+}
+
 .dropdown-container {
   display: inline-block;
   vertical-align: top;
index 315f979f3efdb2c215d8de56ab66609ca8e11e48..c78e13446129644f1ea9501c3b177435f281983e 100644 (file)
@@ -11,6 +11,7 @@ table {
     border: 1px solid #DDD;
     overflow: auto;
     line-height: 1.2;
+       word-break: break-word;
   }
   td p, th p {
     margin: 0;
index 4322cb5a606da731f00a692ed97f1c8ee325a723..7a0987c66c1a1f1c87d71d210e32eb698345270a 100644 (file)
@@ -112,6 +112,13 @@ a {
   }
 }
 
+a.no-link-style {
+  color: inherit;
+  &:hover {
+    text-decoration: none;
+  }
+}
+
 .blended-links a {
   color: inherit;
   svg {
@@ -135,6 +142,9 @@ hr {
   &.faded {
     background-image: linear-gradient(to right, #FFF, #e3e0e0 20%, #e3e0e0 80%, #FFF);
   }
+  &.darker {
+    @include lightDark(background, #DDD, #666);
+  }
   &.margin-top, &.even {
     margin-top: $-l;
   }
index 278e5b6c500af3a42ebc2c532c40abd0e45b326e..1a8b34c5b9af119865929d1eebde78c8180d2a99 100644 (file)
@@ -1,16 +1,14 @@
+@use "sass:math";
 @import "variables";
 @import "mixins";
-@import "spacing";
 @import "html";
 @import "text";
 @import "layout";
 @import "blocks";
 @import "tables";
-@import "header";
 @import "lists";
 @import "pages";
 
-
 html, body {
   background-color: #FFF;
 }
@@ -40,4 +38,25 @@ pre:after {
 }
 pre code {
   white-space: pre-wrap;
+}
+
+.page-break {
+  page-break-after: always;
+}
+@media screen {
+  .page-break {
+    border-top: 1px solid #DDD;
+  }
+}
+
+ul.contents ul li {
+  list-style: circle;
+}
+
+.chapter-hint {
+  color: #888;
+  margin-top: 32px;
+}
+.chapter-hint + h1 {
+  margin-top: 0;
 }
\ No newline at end of file
index 5cbd7f9d5e226b9c4710acaa3b5012862ea77c63..2c51bd75c69027122b0a1f395906170eebb4fa63 100644 (file)
@@ -1,3 +1,4 @@
+@use "sass:math";
 @import "variables";
 
 header {
index 743db9888f771b69bc42d429cbfb8d81e88e1031..582bf7c7569a7faa4a55b7fc1d159e31cd490db6 100644 (file)
@@ -1,3 +1,5 @@
+@use "sass:math";
+
 @import "reset";
 @import "variables";
 @import "mixins";
@@ -109,8 +111,8 @@ $btt-size: 40px;
   color: #FFF;
   fill: #FFF;
   svg {
-    width: $btt-size / 1.5;
-    height: $btt-size / 1.5;
+    width: math.div($btt-size, 1.5);
+    height: math.div($btt-size, 1.5);
     margin-inline-end: 4px;
   }
   width: $btt-size;
@@ -134,6 +136,23 @@ $btt-size: 40px;
   }
 }
 
+.skip-to-content-link {
+  position: fixed;
+  top: -$-xxl;
+  left: 0;
+  background-color: #FFF;
+  z-index: 15;
+  border-radius: 0 4px 4px 0;
+  display: block;
+  box-shadow: $bs-dark;
+  font-weight: bold;
+  &:focus {
+    top: $-xl;
+    outline-offset: -10px;
+    outline: 2px dotted var(--color-primary);
+  }
+}
+
 .contained-search-box {
   display: flex;
   height: 38px;
index d9c3d659513507c952e3aaecac81d6f7d6c6df06..56f7135c36bca4776474eaa839bb6d5aa80e81fc 100644 (file)
                             <h5 id="{{ $endpoint['name'] }}" class="text-mono mb-m">
                                 <span class="api-method" data-method="{{ $endpoint['method'] }}">{{ $endpoint['method'] }}</span>
                                 @if($endpoint['controller_method_kebab'] === 'list')
-                                    <a style="color: inherit;" target="_blank" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
+                                    <a style="color: inherit;" target="_blank" rel="noopener" href="{{ url($endpoint['uri']) }}">{{ url($endpoint['uri']) }}</a>
                                 @else
                                     {{ url($endpoint['uri']) }}
                                 @endif
index 8c9be8290025932277c6f1c1436e67faa7479381..f0a1354ea1e513adda8ad44b1c8e03ed63c0a2a2 100644 (file)
@@ -1,8 +1,10 @@
-@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
+<div component="attachments-list">
+    @foreach($attachments as $attachment)
+        <div class="attachment icon-list">
+            <a class="icon-list-item py-xs attachment-{{ $attachment->external ? 'link' : 'file' }}" href="{{ $attachment->getUrl() }}" @if($attachment->external) target="_blank" @endif>
+                <span class="icon">@icon($attachment->external ? 'export' : 'file')</span>
+                <span>{{ $attachment->name }}</span>
+            </a>
+        </div>
+    @endforeach
+</div>
\ No newline at end of file
index 313faa5755f34e9aebda614f47f563a762911956..b48fde9c01d90c0efe6341fcf88c7ec5d1b5d8a4 100644 (file)
@@ -7,7 +7,7 @@
              class="card drag-card">
             <div class="handle">@icon('grip')</div>
             <div class="py-s">
-                <a href="{{ $attachment->getUrl() }}" target="_blank">{{ $attachment->name }}</a>
+                <a href="{{ $attachment->getUrl() }}" target="_blank" rel="noopener">{{ $attachment->name }}</a>
             </div>
             <div class="flex-fill justify-flex-end">
                 <button component="event-emit-select"
index 7d6595894fa5222c974e670415bd7dceddc04b3f..1afd2d9bb6d6540b659a3aa9bb32ae48d1b76127 100644 (file)
@@ -2,7 +2,7 @@
     {!! csrf_field() !!}
 
     <div>
-        <button id="saml-login" class="button outline block svg">
+        <button id="saml-login" class="button outline svg">
             @icon('saml2')
             <span>{{ trans('auth.log_in_with', ['socialDriver' => config('saml2.name')]) }}</span>
         </button>
index 29e4acee7bf8355f7bbda9b17f240e1483cf001f..dc665a888046ddb8d05d46440f5600c119a5a692 100644 (file)
     <meta name="base-url" content="{{ url('/') }}">
     <meta charset="utf-8">
 
+    <!-- Social Cards Meta -->
+    <meta property="og:title" content="{{ isset($pageTitle) ? $pageTitle . ' | ' : '' }}{{ setting('app-name') }}">
+    <meta property="og:url" content="{{ url()->current() }}">
+    @stack('social-meta')
+    
+
     <!-- Styles and Fonts -->
     <link rel="stylesheet" href="{{ versioned_asset('dist/styles.css') }}">
     <link rel="stylesheet" media="print" href="{{ versioned_asset('dist/print-styles.css') }}">
 </head>
 <body class="@yield('body-class')">
 
+    @include('common.parts.skip-to-content')
     @include('partials.notifications')
     @include('common.header')
 
-    <div id="content" class="block">
+    <div id="content" components="@yield('content-components')" class="block">
         @yield('content')
     </div>
 
index f62b895827b7c87b0a3f47ed82d66037fd1d5ad1..9cd5618a53f3be19fe609fefdeadb3e6e1fed4f0 100644 (file)
@@ -1,38 +1,8 @@
-<!doctype html>
-<html lang="{{ config('app.lang') }}">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-    <title>{{ $book->name }}</title>
+@extends('export-layout')
 
-    @include('partials.export-styles', ['format' => $format])
-
-    <style>
-        .page-break {
-            page-break-after: always;
-        }
-        .chapter-hint {
-            color: #888;
-            margin-top: 32px;
-        }
-        .chapter-hint + h1 {
-            margin-top: 0;
-        }
-        ul.contents ul li {
-            list-style: circle;
-        }
-        @media screen {
-            .page-break {
-                border-top: 1px solid #DDD;
-            }
-        }
-    </style>
-    @yield('head')
-    @include('partials.custom-head')
-</head>
-<body>
-
-<div class="page-content">
+@section('title', $book->name)
 
+@section('content')
     <h1 style="font-size: 4.8em">{{$book->name}}</h1>
 
     <p>{{ $book->description }}</p>
@@ -73,8 +43,4 @@
         @endif
 
     @endforeach
-
-</div>
-
-</body>
-</html>
+@endsection
\ No newline at end of file
index 840d0604c8e0c22185d6e33ea43eac02e880ff88..9fdcaea61be259070a57dcc0d76b1800a280e4bc 100644 (file)
@@ -2,7 +2,7 @@
 {{ csrf_field() }}
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form.text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name', 'autofocus' => true])
 </div>
 
 <div class="form-group description-input">
index def198bddac2450b34958534cd4572da01ee0ba5..5879cf6a22277283180e6b3782564618bf0640b5 100644 (file)
@@ -6,6 +6,13 @@
     option:entity-search:entity-type="book"
 @stop
 
+@push('social-meta')
+    <meta property="og:description" content="{{ Str::limit($book->description, 100, '...') }}">
+    @if($book->cover)
+        <meta property="og:image" content="{{ $book->getBookCover() }}">
+    @endif
+@endpush
+
 @section('body')
 
     <div class="mb-s">
 
             <hr class="primary-background">
 
+            @if(signedInUser())
+                @include('partials.entity-favourite-action', ['entity' => $book])
+            @endif
             @include('partials.entity-export-menu', ['entity' => $book])
         </div>
     </div>
index 506e8db3d40f7c18b5c7ca60fefb3ca4fe0c5ff6..18f056f27f175570b8546be9322bc858afecbd19 100644 (file)
@@ -1,30 +1,8 @@
-<!doctype html>
-<html lang="{{ config('app.lang') }}">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-    <title>{{ $chapter->name }}</title>
+@extends('export-layout')
 
-    @include('partials.export-styles', ['format' => $format])
-
-    <style>
-        .page-break {
-            page-break-after: always;
-        }
-        ul.contents ul li {
-            list-style: circle;
-        }
-        @media screen {
-            .page-break {
-                border-top: 1px solid #DDD;
-            }
-        }
-    </style>
-    @include('partials.custom-head')
-</head>
-<body>
-
-<div class="page-content">
+@section('title', $chapter->name)
 
+@section('content')
     <h1 style="font-size: 4.8em">{{$chapter->name}}</h1>
 
     <p>{{ $chapter->description }}</p>
@@ -42,8 +20,4 @@
         <h1 id="page-{{$page->id}}">{{ $page->name }}</h1>
         {!! $page->html !!}
     @endforeach
-
-</div>
-
-</body>
-</html>
+@endsection
\ No newline at end of file
index 60cfe6674f1b7eeaf7382575a24e970264698593..0de665744474c40a20c918741ccf7bfd71070062 100644 (file)
@@ -3,7 +3,7 @@
 
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form.text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name', 'autofocus' => true])
 </div>
 
 <div class="form-group description-input">
index db02ebcc4f9ae6b6d686a591a8eb2398f5d905d8..e8e0f6374fe24ccdd33f97300d54919bb8861969 100644 (file)
@@ -6,6 +6,10 @@
     option:entity-search:entity-type="chapter"
 @stop
 
+@push('social-meta')
+    <meta property="og:description" content="{{ Str::limit($chapter->description, 100, '...') }}">
+@endpush
+
 @section('body')
 
     <div class="mb-m print-hidden">
@@ -52,6 +56,8 @@
         @include('partials.entity-search-results')
     </main>
 
+    @include('partials.entity-sibling-navigation', ['next' => $next, 'previous' => $previous])
+
 @stop
 
 @section('right')
 
             <hr class="primary-background"/>
 
+            @if(signedInUser())
+                @include('partials.entity-favourite-action', ['entity' => $chapter])
+            @endif
             @include('partials.entity-export-menu', ['entity' => $chapter])
         </div>
     </div>
similarity index 82%
rename from resources/views/pages/detailed-listing.blade.php
rename to resources/views/common/detailed-listing-paginated.blade.php
index c2bbdb53711629d86bc7a3272f8fcab12ff6ba20..af9490a410f897a62891230268130591f8e253c3 100644 (file)
@@ -6,11 +6,11 @@
             <h1 class="list-heading">{{ $title }}</h1>
 
             <div class="book-contents">
-                @include('partials.entity-list', ['entities' => $pages, 'style' => 'detailed'])
+                @include('partials.entity-list', ['entities' => $entities, 'style' => 'detailed'])
             </div>
 
             <div class="text-center">
-                {!! $pages->links() !!}
+                {!! $entities->links() !!}
             </div>
         </main>
     </div>
diff --git a/resources/views/common/detailed-listing-with-more.blade.php b/resources/views/common/detailed-listing-with-more.blade.php
new file mode 100644 (file)
index 0000000..5790f20
--- /dev/null
@@ -0,0 +1,19 @@
+@extends('simple-layout')
+
+@section('body')
+    <div class="container small pt-xl">
+        <main class="card content-wrap">
+            <h1 class="list-heading">{{ $title }}</h1>
+
+            <div class="book-contents">
+                @include('partials.entity-list', ['entities' => $entities, 'style' => 'detailed'])
+            </div>
+
+            <div class="text-right">
+                @if($hasMoreLink)
+                    <a href="{{ $hasMoreLink }}" class="button outline">{{ trans('common.more') }}</a>
+                @endif
+            </div>
+        </main>
+    </div>
+@stop
\ No newline at end of file
index 67b52a609e686881e6286c9f7ad028ab6f8b494a..dd488dce541a68a7194d113a73fe9ce1a8ea32cb 100644 (file)
@@ -1,7 +1,7 @@
 @if(count(setting('app-footer-links', [])) > 0)
 <footer>
     @foreach(setting('app-footer-links', []) as $link)
-        <a href="{{ $link['url'] }}" target="_blank">{{ strpos($link['label'], 'trans::') === 0 ? trans(str_replace('trans::', '', $link['label'])) : $link['label'] }}</a>
+        <a href="{{ $link['url'] }}" target="_blank" rel="noopener">{{ strpos($link['label'], 'trans::') === 0 ? trans(str_replace('trans::', '', $link['label'])) : $link['label'] }}</a>
     @endforeach
 </footer>
 @endif
\ No newline at end of file
index 52f6b8cbb94b86e386a0120b926d846cfe115569..274a09996125f21303d4d38a58b91f4214fe65f1 100644 (file)
@@ -1,4 +1,4 @@
-<header id="header" header-mobile-toggle class="primary-background">
+<header id="header" component="header-mobile-toggle" class="primary-background">
     <div class="grid mx-l">
 
         <div>
                     <span class="logo-text">{{ setting('app-name') }}</span>
                 @endif
             </a>
-            <div class="mobile-menu-toggle hide-over-l">@icon('more')</div>
+            <button type="button"
+                    refs="header-mobile-toggle@toggle"
+                    title="{{ trans('common.header_menu_expand') }}"
+                    aria-expanded="false"
+                    class="mobile-menu-toggle hide-over-l">@icon('more')</button>
         </div>
 
         <div class="flex-container-row justify-center hide-under-l">
@@ -25,7 +29,7 @@
         </div>
 
         <div class="text-right">
-            <nav class="header-links">
+            <nav refs="header-mobile-toggle@menu" class="header-links">
                 <div class="links text-center">
                     @if (hasAppAccess())
                         <a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
                 </div>
                 @if(signedInUser())
                     <?php $currentUser = user(); ?>
-                    <div class="dropdown-container" component="dropdown">
+                    <div class="dropdown-container" component="dropdown" option:dropdown:bubble-escapes="true">
                         <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 refs="dropdown@menu" class="dropdown-menu" role="menu">
+                            <li>
+                                <a href="{{ url('/favourites') }}">@icon('star'){{ trans('entities.my_favourites') }}</a>
+                            </li>
                             <li>
                                 <a href="{{ $currentUser->getProfileUrl() }}">@icon('user'){{ trans('common.view_profile') }}</a>
                             </li>
index 1c18edb246bc1ce93040c7af6325d2a0250647c1..9f62d21e7b1c4e1f708e5fc7230f00faf765c8e3 100644 (file)
@@ -19,8 +19,8 @@
                 </a>
             @endif
             @include('partials.view-toggle', ['view' => $view, 'type' => 'books'])
-            @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
+            @include('components.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
         </div>
     </div>
 @stop
index e0820305746c799d5d2deb890849397b98f1f70d..a22e2600228ceac320eb57b78fb965d52a159d4a 100644 (file)
@@ -18,8 +18,8 @@
     <div class="actions mb-xl">
         <h5>{{ trans('common.actions') }}</h5>
         <div class="icon-list text-primary">
-            @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
+            @include('components.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
         </div>
     </div>
 @stop
\ No newline at end of file
index 957fa6578fffd35e349c550fdf521a237fd09440..3c32fed740f7c70cb75f0da56b1eb8842eea6cff 100644 (file)
@@ -19,8 +19,8 @@
                 </a>
             @endif
             @include('partials.view-toggle', ['view' => $view, 'type' => 'shelves'])
-            @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
-            @include('partials.dark-mode-toggle', ['classes' => 'text-muted icon-list-item text-primary'])
+            @include('components.expand-toggle', ['classes' => 'text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+            @include('partials.dark-mode-toggle', ['classes' => 'icon-list-item text-primary'])
         </div>
     </div>
 @stop
index 4c36ce61a9be9648f48dbc68462b2b75bc73f83a..7fe0659f0bdb5e42d824e6a981cb0f9d033f21bc 100644 (file)
@@ -5,6 +5,18 @@
     </div>
 @endif
 
+@if(count($favourites) > 0)
+    <div id="top-favourites" class="mb-xl">
+        <h5>
+            <a href="{{ url('/favourites') }}" class="no-color">{{ trans('entities.my_most_viewed_favourites') }}</a>
+        </h5>
+        @include('partials.entity-list', [
+            'entities' => $favourites,
+            'style' => 'compact',
+        ])
+    </div>
+@endif
+
 <div class="mb-xl">
     <h5>{{ trans('entities.' . (auth()->check() ? 'my_recently_viewed' : 'books_recent')) }}</h5>
     @include('partials.entity-list', [
index ad503463e46f1db404882fbf58018dfae39b227c..187f222b50f1b7b524f9b899d90dd7fb74fade0a 100644 (file)
@@ -6,7 +6,7 @@
         <div class="grid half">
             <div>
                 <div class="icon-list inline block">
-                    @include('components.expand-toggle', ['target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
+                    @include('components.expand-toggle', ['classes' => 'text-muted text-primary', 'target' => '.entity-list.compact .entity-item-snippet', 'key' => 'home-details'])
                 </div>
             </div>
             <div class="text-m-right">
             </div>
 
             <div>
+                @if(count($favourites) > 0)
+                    <div id="top-favourites" class="card mb-xl">
+                        <h3 class="card-title">
+                            <a href="{{ url('/favourites') }}" class="no-color">{{ trans('entities.my_most_viewed_favourites') }}</a>
+                        </h3>
+                        <div class="px-m">
+                            @include('partials.entity-list', [
+                            'entities' => $favourites,
+                            'style' => 'compact',
+                            ])
+                        </div>
+                    </div>
+                @endif
+
                 <div id="recent-pages" class="card mb-xl">
                     <h3 class="card-title"><a class="no-color" href="{{ url("/pages/recently-updated") }}">{{ trans('entities.recently_updated_pages') }}</a></h3>
                     <div id="recently-updated-pages" class="px-m">
diff --git a/resources/views/common/parts/skip-to-content.blade.php b/resources/views/common/parts/skip-to-content.blade.php
new file mode 100644 (file)
index 0000000..b63573d
--- /dev/null
@@ -0,0 +1 @@
+<a class="px-m py-s skip-to-content-link" href="#main-content">{{ trans('common.skip_to_main_content') }}</a>
\ No newline at end of file
index 0c14490386b0cfb968c00f7d06a535ab2570a6ea..8ed7ff6e036167cb97a7428a57bb906d2f2e7f1e 100644 (file)
@@ -6,7 +6,7 @@ $key - Unique key for checking existing stored state.
 <button type="button" expand-toggle="{{ $target }}"
    expand-toggle-update-endpoint="{{ url('/settings/users/'. user()->id .'/update-expansion-preference/' . $key) }}"
    expand-toggle-is-open="{{ $isOpen ? 'yes' : 'no' }}"
-   class="text-muted icon-list-item text-primary">
+   class="icon-list-item {{ $classes ?? '' }}">
     <span>@icon('expand-text')</span>
     <span>{{ trans('common.toggle_details') }}</span>
 </button>
index e49a5fca723f8c96fbc1182856b4dae676ac15a8..6d62552266945fc7d27579cab7044a835289907f 100644 (file)
@@ -7,7 +7,7 @@
           option:ajax-form:url="{{ url('images/' . $image->id) }}">
 
         <div class="image-manager-viewer">
-            <a href="{{ $image->url }}" target="_blank" class="block">
+            <a href="{{ $image->url }}" target="_blank" rel="noopener" class="block">
                 <img src="{{ $image->thumbs['display'] }}"
                      alt="{{ $image->name }}"
                      class="anim fadeIn"
@@ -40,6 +40,7 @@
                     <li>
                         <a href="{{ $page->url }}"
                            target="_blank"
+                           rel="noopener"
                            class="text-neg">{{ $page->name }}</a>
                     </li>
                 @endforeach
index c59615d92a30a38fbb0aa66feba92d5a56c0ad1f..0df42e3cef9993f12c7552881a44a76ebbfbcb03 100644 (file)
@@ -3,7 +3,7 @@
 <div page-picker>
     <div class="input-base">
         <span @if($value) style="display: none" @endif page-picker-default class="text-muted italic">{{ $placeholder }}</span>
-        <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::find($value)->name : '' }}</a>
+        <a @if(!$value) style="display: none" @endif href="{{ url('/link/' . $value) }}" target="_blank" rel="noopener" class="text-page" page-picker-display>#{{$value}}, {{$value ? \BookStack\Entities\Models\Page::find($value)->name : '' }}</a>
     </div>
     <br>
     <input type="hidden" value="{{$value}}" name="{{$name}}" id="{{$name}}">
index f7a9c6c48623c36d92f9e0434532a1388f5753c7..ffbd5c3303f46cebb93795f6b58f210886b51293 100644 (file)
@@ -1,6 +1,11 @@
 @foreach($entity->tags as $tag)
     <div class="tag-item primary-background-light">
-        <div class="tag-name"><a href="{{ url('/search?term=%5B' . urlencode($tag->name) .'%5D') }}">@icon('tag'){{ $tag->name }}</a></div>
-        @if($tag->value) <div class="tag-value"><a href="{{ url('/search?term=%5B' . urlencode($tag->name) .'%3D' . urlencode($tag->value) . '%5D') }}">{{$tag->value}}</a></div> @endif
+        @if($linked ?? true)
+            <div class="tag-name"><a href="{{ $tag->nameUrl() }}">@icon('tag'){{ $tag->name }}</a></div>
+            @if($tag->value) <div class="tag-value"><a href="{{ $tag->valueUrl() }}">{{$tag->value}}</a></div> @endif
+        @else
+            <div class="tag-name"><span>@icon('tag'){{ $tag->name }}</span></div>
+            @if($tag->value) <div class="tag-value"><span>{{$tag->value}}</span></div> @endif
+        @endif
     </div>
 @endforeach
\ No newline at end of file
index 02f97fc546fcbc195ec04421731fba44b15cfb1d..c4a5dc78276b27be5f6ac1470b73caeb1e8fa624 100644 (file)
@@ -6,9 +6,11 @@
     <div class="card mb-xl px-l pb-l pt-l">
         <div class="grid half v-center">
             <div>
-                <h1 class="list-heading">{{ $message ?? trans('errors.404_page_not_found') }}</h1>
-                <h5>{{ trans('errors.sorry_page_not_found') }}</h5>
-                <p>{{ trans('errors.sorry_page_not_found_permission_warning') }}</p>
+                @include('errors.parts.not-found-text', [
+                    'title' => $message ?? trans('errors.404_page_not_found'),
+                    'subtitle' => $subtitle ?? trans('errors.sorry_page_not_found'),
+                    'details' => $details ?? trans('errors.sorry_page_not_found_permission_warning'),
+                ])
             </div>
             <div class="text-right">
                 @if(!signedInUser())
@@ -26,7 +28,7 @@
                 <div class="card mb-xl">
                     <h3 class="card-title">{{ trans('entities.pages_popular') }}</h3>
                     <div class="px-m">
-                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, ['page']), 'style' => 'compact'])
+                        @include('partials.entity-list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['page']), 'style' => 'compact'])
                     </div>
                 </div>
             </div>
@@ -34,7 +36,7 @@
                 <div class="card mb-xl">
                     <h3 class="card-title">{{ trans('entities.books_popular') }}</h3>
                     <div class="px-m">
-                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, ['book']), 'style' => 'compact'])
+                        @include('partials.entity-list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['book']), 'style' => 'compact'])
                     </div>
                 </div>
             </div>
@@ -42,7 +44,7 @@
                 <div class="card mb-xl">
                     <h3 class="card-title">{{ trans('entities.chapters_popular') }}</h3>
                     <div class="px-m">
-                        @include('partials.entity-list', ['entities' => Views::getPopular(10, 0, ['chapter']), 'style' => 'compact'])
+                        @include('partials.entity-list', ['entities' => (new \BookStack\Entities\Queries\Popular)->run(10, 0, ['chapter']), 'style' => 'compact'])
                     </div>
                 </div>
             </div>
index d06ddbc574a2707542a96d97349f86c473d36fdc..ad759b49dc60bc18139a71022d5b99d42ef2083f 100644 (file)
@@ -5,7 +5,7 @@
     <div class="container small py-xl">
 
         <main class="card content-wrap auto-height">
-            <div class="body">
+            <div id="main-content" class="body">
                 <h3>{{ trans('errors.error_occurred') }}</h3>
                 <h5 class="mb-m">{{ $message ?? 'An unknown error occurred' }}</h5>
                 <p><a href="{{ url('/') }}" class="button outline">{{ trans('errors.return_home') }}</a></p>
diff --git a/resources/views/errors/parts/not-found-text.blade.php b/resources/views/errors/parts/not-found-text.blade.php
new file mode 100644 (file)
index 0000000..5b107b2
--- /dev/null
@@ -0,0 +1,5 @@
+{{--The below text may be dynamic based upon language and scenario.--}}
+{{--It's safer to add new text sections here rather than altering existing ones.--}}
+<h1 class="list-heading">{{ $title }}</h1>
+<h5>{{ $subtitle }}</h5>
+<p>{{ $details }}</p>
\ No newline at end of file
diff --git a/resources/views/export-layout.blade.php b/resources/views/export-layout.blade.php
new file mode 100644 (file)
index 0000000..f23b3cc
--- /dev/null
@@ -0,0 +1,15 @@
+<!doctype html>
+<html lang="{{ config('app.lang') }}">
+<head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
+    <title>@yield('title')</title>
+
+    @include('partials.export-styles', ['format' => $format])
+    @include('partials.export-custom-head')
+</head>
+<body>
+<div class="page-content">
+    @yield('content')
+</div>
+</body>
+</html>
\ No newline at end of file
index 6cf5ab8bdcac9db8b02f3debb384005ddaa27c80..d3e89cc447fa07953ac30599e27d9b625ec70c27 100644 (file)
@@ -25,7 +25,7 @@
     <table permissions-table class="table permissions-table toggle-switch-list" style="{{ !$model->restricted ? 'display: none' : '' }}">
         <tr>
             <th>{{ trans('common.role') }}</th>
-            <th @if($model->isA('page')) colspan="3" @else colspan="4" @endif>
+            <th colspan="{{ $model->isA('page') ? '3' : '4'  }}">
                 {{ trans('common.actions') }}
                 <a href="#" permissions-table-toggle-all class="text-small ml-m text-primary">{{ trans('common.toggle_all') }}</a>
             </th>
index f580b06cf7cd1caeee8a0bb624e5ba20a8411d10..2120bddb233a7d457bb7fd83d60dbf5449c8c0dd 100644 (file)
@@ -8,7 +8,7 @@
 
 @section('content')
 
-    <div class="flex-fill flex fill-height">
+    <div id="main-content" class="flex-fill flex fill-height">
         <form action="{{ $page->getUrl() }}" autocomplete="off" data-page-id="{{ $page->id }}" method="POST" class="flex flex-fill">
             {{ csrf_field() }}
 
index 47a4d870a041be8e10d4a11704e5b2bd9a4bb1d2..74d17c128f794be4c7857c6eb584c07211a3a534 100644 (file)
@@ -1,51 +1,13 @@
-<!doctype html>
-<html lang="{{ config('app.lang') }}">
-<head>
-    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
-    <title>{{ $page->name }}</title>
+@extends('export-layout')
 
-    @include('partials.export-styles', ['format' => $format])
+@section('title', $page->name)
 
-    @if($format === 'pdf')
-        <style>
-            body {
-                font-size: 14px;
-                line-height: 1.2;
-            }
+@section('content')
+    @include('pages.page-display')
 
-            h1, h2, h3, h4, h5, h6 {
-                line-height: 1.2;
-            }
-
-            table {
-                max-width: 800px !important;
-                font-size: 0.8em;
-                width: 100% !important;
-            }
-
-            table td {
-                width: auto !important;
-            }
-        </style>
-    @endif
-
-    @include('partials.custom-head')
-</head>
-<body>
-
-<div id="page-show">
-    <div class="page-content">
-
-        @include('pages.page-display')
-
-        <hr>
-
-        <div class="text-muted text-small">
-            @include('partials.entity-export-meta', ['entity' => $page])
-        </div>
+    <hr>
 
+    <div class="text-muted text-small">
+        @include('partials.entity-export-meta', ['entity' => $page])
     </div>
-</div>
-
-</body>
-</html>
+@endsection
\ No newline at end of file
index 017a971ff1769ab6c57c33e511477f2e3e55d1fc..39d628e17d0ce6ec503d6f8ea76051fa7d1238a2 100644 (file)
@@ -2,6 +2,7 @@
      option:markdown-editor:page-id="{{ $model->id ?? 0 }}"
      option:markdown-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
      option:markdown-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
+     option:markdown-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
      class="flex-fill flex code-fill">
 
     <div class="markdown-editor-wrap active">
index 6ff33c68de4b7e2baddc4db1301f111a0161c865..6624620c5e0d46d3d6da4120d01e841e89939965 100644 (file)
                             <td><small>{{ $revision->created_at->formatLocalized('%e %B %Y %H:%M:%S') }} <br> ({{ $revision->created_at->diffForHumans() }})</small></td>
                             <td>{{ $revision->summary }}</td>
                             <td class="actions">
-                                <a href="{{ $revision->getUrl('changes') }}" target="_blank">{{ trans('entities.pages_revisions_changes') }}</a>
+                                <a href="{{ $revision->getUrl('changes') }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_changes') }}</a>
                                 <span class="text-muted">&nbsp;|&nbsp;</span>
 
 
                                 @if ($index === 0)
-                                    <a target="_blank" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
+                                    <a target="_blank" rel="noopener" href="{{ $page->getUrl() }}"><i>{{ trans('entities.pages_revisions_current') }}</i></a>
                                 @else
-                                    <a href="{{ $revision->getUrl() }}" target="_blank">{{ trans('entities.pages_revisions_preview') }}</a>
+                                    <a href="{{ $revision->getUrl() }}" target="_blank" rel="noopener">{{ trans('entities.pages_revisions_preview') }}</a>
                                     <span class="text-muted">&nbsp;|&nbsp;</span>
                                     <div component="dropdown" class="dropdown-container">
                                         <a refs="dropdown@toggle" href="#" aria-haspopup="true" aria-expanded="false">{{ trans('entities.pages_revisions_restore') }}</a>
index 13125464a7114396012f3685fa56e38ae23b81b9..012454e7c4b8abf1dc464ebad37f747471711beb 100644 (file)
@@ -1,5 +1,9 @@
 @extends('tri-layout')
 
+@push('social-meta')
+    <meta property="og:description" content="{{ Str::limit($page->text, 100, '...') }}">
+@endpush
+
 @section('body')
 
     <div class="mb-m print-hidden">
         </div>
     </main>
 
+    @include('partials.entity-sibling-navigation', ['next' => $next, 'previous' => $previous])
+
     @if ($commentsEnabled)
-        <div class="container small p-none comments-container mb-l print-hidden">
+        @if(($previous || $next))
+            <div class="px-xl">
+                <hr class="darker">
+            </div>
+        @endif
+
+        <div class="px-xl comments-container mb-l print-hidden">
             @include('comments.comments', ['page' => $page])
             <div class="clearfix"></div>
         </div>
 
             <hr class="primary-background"/>
 
-            {{--Export--}}
+            @if(signedInUser())
+                @include('partials.entity-favourite-action', ['entity' => $page])
+            @endif
             @include('partials.entity-export-menu', ['entity' => $page])
         </div>
 
index d8b8b1c353c73f53c10b639635345fe78c86ee14..02948fa2ecd18ed01d2d60754964d7e0207184a3 100644 (file)
@@ -2,6 +2,7 @@
      option:wysiwyg-editor:page-id="{{ $model->id ?? 0 }}"
      option:wysiwyg-editor:text-direction="{{ config('app.rtl') ? 'rtl' : 'ltr' }}"
      option:wysiwyg-editor:image-upload-error-text="{{ trans('errors.image_upload_error') }}"
+     option:wysiwyg-editor:server-upload-limit-text="{{ trans('errors.server_upload_limit') }}"
      class="flex-fill flex">
 
     <textarea id="html-editor"  name="html" rows="5"
diff --git a/resources/views/partials/custom-head-content.blade.php b/resources/views/partials/custom-head-content.blade.php
deleted file mode 100644 (file)
index b245b7a..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-@if(setting('app-custom-head', false))
-    <!-- Custom user content -->
-    {!! setting('app-custom-head') !!}
-    <!-- End custom user content -->
-@endif
\ No newline at end of file
index dd7cc41e43a2f81da99d4bef76362294c71c3d8b..fa5ba0cc456333776de144372098e68fc7f3fe65 100644 (file)
@@ -1,5 +1,5 @@
 @if(setting('app-custom-head') && \Route::currentRouteName() !== 'settings')
-    <!-- Custom user content -->
-    {!! setting('app-custom-head') !!}
-    <!-- End custom user content -->
+<!-- Custom user content -->
+{!! setting('app-custom-head') !!}
+<!-- End custom user content -->
 @endif
\ No newline at end of file
index 4d847bcaef801c4700a74a84de8fc7a025fc8fe6..2b0f5c19dd84b9b88130afe5ee289545eae0100a 100644 (file)
@@ -5,8 +5,9 @@
         <span>{{ trans('entities.export') }}</span>
     </div>
     <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>
+        <li><a href="{{ $entity->getUrl('/export/html') }}" target="_blank" rel="noopener">{{ trans('entities.export_html') }} <span class="text-muted float right">.html</span></a></li>
+        <li><a href="{{ $entity->getUrl('/export/pdf') }}" target="_blank" rel="noopener">{{ trans('entities.export_pdf') }} <span class="text-muted float right">.pdf</span></a></li>
+        <li><a href="{{ $entity->getUrl('/export/plaintext') }}" target="_blank" rel="noopener">{{ trans('entities.export_text') }} <span class="text-muted float right">.txt</span></a></li>
+        <li><a href="{{ $entity->getUrl('/export/markdown') }}" target="_blank" rel="noopener">{{ trans('entities.export_md') }} <span class="text-muted float right">.md</span></a></li>
     </ul>
-</div>
\ No newline at end of file
+</div>
index a84d0ae85eb277d6e0c65dc39f61059f37bc5841..02a39e78c9218873251da588345113516ab98b0c 100644 (file)
@@ -4,13 +4,13 @@
     @endif
 
     @icon('star'){!! trans('entities.meta_created' . ($entity->createdBy ? '_name' : ''), [
-        'timeLength' => $entity->created_at->toDayDateTimeString(),
-        'user' => htmlentities($entity->createdBy->name),
+        'timeLength' => $entity->created_at->formatLocalized('%e %B %Y %H:%M:%S'),
+        'user' => e($entity->createdBy->name ?? ''),
         ]) !!}
     <br>
 
     @icon('edit'){!! trans('entities.meta_updated' . ($entity->updatedBy ? '_name' : ''), [
-            'timeLength' => $entity->updated_at->toDayDateTimeString(),
-            'user' => htmlentities($entity->updatedBy->name)
+            'timeLength' => $entity->updated_at->formatLocalized('%e %B %Y %H:%M:%S'),
+            'user' => e($entity->updatedBy->name ?? '')
         ]) !!}
 </div>
\ No newline at end of file
diff --git a/resources/views/partials/entity-favourite-action.blade.php b/resources/views/partials/entity-favourite-action.blade.php
new file mode 100644 (file)
index 0000000..49ba6aa
--- /dev/null
@@ -0,0 +1,12 @@
+@php
+ $isFavourite = $entity->isFavourite();
+@endphp
+<form action="{{ url('/favourites/' . ($isFavourite ? 'remove' : 'add')) }}" method="POST">
+    {{ csrf_field() }}
+    <input type="hidden" name="type" value="{{ get_class($entity) }}">
+    <input type="hidden" name="id" value="{{ $entity->id }}">
+    <button type="submit" class="icon-list-item text-primary">
+        <span>@icon($isFavourite ? 'star' : 'star-outline')</span>
+        <span>{{ $isFavourite ? trans('common.unfavourite') : trans('common.favourite') }}</span>
+    </button>
+</form>
\ No newline at end of file
index d42b1967fcd230a83537cbe76ef6caae369ff184..d605953c77fd38541ff18e976ea81433e3329b52 100644 (file)
@@ -1,4 +1,5 @@
 @component('partials.entity-list-item-basic', ['entity' => $entity])
+
 <div class="entity-item-snippet">
 
     @if($showPath ?? false)
 
     <p class="text-muted break-text">{{ $entity->getExcerpt() }}</p>
 </div>
+
+@if(($showTags ?? false) && $entity->tags->count() > 0)
+    <div class="entity-item-tags mt-xs">
+        @include('components.tag-list', ['entity' => $entity, 'linked' => false ])
+    </div>
+@endif
+
 @endcomponent
\ No newline at end of file
index be826f1ac4f495ba2f61d2eff9cdfd779c44d4fa..393f4e8a792c5af7f92afc92020bb4c599bcb598 100644 (file)
@@ -1,7 +1,7 @@
 @if(count($entities) > 0)
     <div class="entity-list {{ $style ?? '' }}">
         @foreach($entities as $index => $entity)
-            @include('partials.entity-list-item', ['entity' => $entity, 'showPath' => $showPath ?? false])
+            @include('partials.entity-list-item', ['entity' => $entity, 'showPath' => $showPath ?? false, 'showTags' => $showTags ?? false])
         @endforeach
     </div>
 @else
index 8996df9bb67d0f491d9a8de5e5d18b56560883fb..298cc7c3e421750c6ec9669b2fcd9fb0a229f319 100644 (file)
@@ -14,7 +14,7 @@
         </div>
     @endif
 
-    @if ($entity->ownedBy && $entity->ownedBy->id !== $entity->createdBy->id)
+    @if ($entity->ownedBy && $entity->owned_by !== $entity->created_by)
         <div>
             @icon('user'){!! trans('entities.meta_owned_name', [
             'user' => "<a href='{$entity->ownedBy->getProfileUrl()}'>".e($entity->ownedBy->name). "</a>"
diff --git a/resources/views/partials/entity-sibling-navigation.blade.php b/resources/views/partials/entity-sibling-navigation.blade.php
new file mode 100644 (file)
index 0000000..1f64bac
--- /dev/null
@@ -0,0 +1,28 @@
+<div id="sibling-navigation" class="grid half collapse-xs items-center mb-m px-m no-row-gap fade-in-when-active print-hidden">
+    <div>
+        @if($previous)
+            <a href="{{  $previous->getUrl()  }}" class="outline-hover no-link-style block rounded">
+                <div class="px-m pt-xs text-muted">{{ trans('common.previous') }}</div>
+                <div class="inline-block">
+                    <div class="icon-list-item no-hover">
+                        <span class="text-{{ $previous->getType() }} ">@icon($previous->getType())</span>
+                        <span>{{ $previous->getShortName(48) }}</span>
+                    </div>
+                </div>
+            </a>
+        @endif
+    </div>
+    <div>
+        @if($next)
+            <a href="{{  $next->getUrl()  }}" class="outline-hover no-link-style block rounded text-xs-right">
+                <div class="px-m pt-xs text-muted text-xs-right">{{ trans('common.next') }}</div>
+                <div class="inline block">
+                    <div class="icon-list-item no-hover">
+                        <span class="text-{{ $next->getType() }} ">@icon($next->getType())</span>
+                        <span>{{ $next->getShortName(48) }}</span>
+                    </div>
+                </div>
+            </a>
+        @endif
+    </div>
+</div>
\ No newline at end of file
diff --git a/resources/views/partials/export-custom-head.blade.php b/resources/views/partials/export-custom-head.blade.php
new file mode 100644 (file)
index 0000000..f428e9f
--- /dev/null
@@ -0,0 +1,5 @@
+@if(setting('app-custom-head'))
+<!-- Custom user content -->
+{!! \BookStack\Util\HtmlContentFilter::removeScripts(setting('app-custom-head')) !!}
+<!-- End custom user content -->
+@endif
\ No newline at end of file
index 52bfda2a6fc40c6c02d745b5079261e88093c017..967dc19ec709264de9302900d967189358aa39e8 100644 (file)
@@ -6,6 +6,27 @@
 
 @if ($format === 'pdf')
     <style>
+
+        /* PDF size adjustments */
+        body {
+            font-size: 14px;
+            line-height: 1.2;
+        }
+
+        h1, h2, h3, h4, h5, h6 {
+            line-height: 1.2;
+        }
+
+        table {
+            max-width: 800px !important;
+            font-size: 0.8em;
+            width: 100% !important;
+        }
+
+        table td {
+            width: auto !important;
+        }
+
         /* Patches for CSS variable colors */
         a {
             color: {{ setting('app-color') }};
index acf214433b2f646710c5b0052ae820d43b944254..49a6c1f653dcaed97e257df2aa15a4314ffd93c3 100644 (file)
@@ -77,7 +77,7 @@
 
                     <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])
+                        @include('partials.entity-list', ['entities' => $entities, 'showPath' => true, 'showTags' => true])
                     </div>
 
                     @if($hasNextPage)
index e635455bfd93a359ae2d3352b716fd9dc399085d..c088bad892aa4e25bd76d7d2f90e0908f21c94d5 100644 (file)
@@ -2,7 +2,7 @@
 
 <div class="form-group title-input">
     <label for="name">{{ trans('common.name') }}</label>
-    @include('form.text', ['name' => 'name'])
+    @include('form.text', ['name' => 'name', 'autofocus' => true])
 </div>
 
 <div class="form-group description-input">
index 7ed36c90685ef1d8a978b7fdf8a4a00d50deec61..f5920d4758d98305b5cf7268349b17c0deb26618 100644 (file)
@@ -1,5 +1,12 @@
 @extends('tri-layout')
 
+@push('social-meta')
+    <meta property="og:description" content="{{ Str::limit($shelf->description, 100, '...') }}">
+    @if($shelf->cover)
+        <meta property="og:image" content="{{ $shelf->getBookCover() }}">
+    @endif
+@endpush
+
 @section('body')
 
     <div class="mb-s">
                 </a>
             @endif
 
+            @if(signedInUser())
+                <hr class="primary-background">
+                @include('partials.entity-favourite-action', ['entity' => $shelf])
+            @endif
+
         </div>
     </div>
 @stop
index a57aaebef64f39f45460c27dfc6d955ff2ce74d4..b7d6d3ccddc9f88e4f524ebfbe2569be1c7e3416 100644 (file)
@@ -4,7 +4,7 @@
 
     <div class="flex-fill flex">
         <div class="content flex">
-            <div class="scroll-body">
+            <div id="main-content" class="scroll-body">
                 @yield('body')
             </div>
         </div>
index 71c546964ef6fb31d89a553256654324998de35b..d985db6499d03f003b23d4b16c4e850966706fe5 100644 (file)
@@ -1,21 +1,31 @@
 @extends('base')
 
 @section('body-class', 'tri-layout')
+@section('content-components', 'tri-layout')
 
 @section('content')
 
-    <div class="tri-layout-mobile-tabs text-primary print-hidden">
+    <div class="tri-layout-mobile-tabs print-hidden">
         <div class="grid half no-break no-gap">
-            <div class="tri-layout-mobile-tab px-m py-s" tri-layout-mobile-tab="info">
+            <button type="button"
+                    refs="tri-layout@tab"
+                    data-tab="info"
+                    aria-label="{{ trans('common.tab_info_label') }}"
+                    class="tri-layout-mobile-tab px-m py-m text-primary">
                 {{ trans('common.tab_info') }}
-            </div>
-            <div class="tri-layout-mobile-tab px-m py-s active" tri-layout-mobile-tab="content">
+            </button>
+            <button type="button"
+                    refs="tri-layout@tab"
+                    data-tab="content"
+                    aria-label="{{ trans('common.tab_content_label') }}"
+                    aria-selected="true"
+                    class="tri-layout-mobile-tab px-m py-m text-primary active">
                 {{ trans('common.tab_content') }}
-            </div>
+            </button>
         </div>
     </div>
 
-    <div class="tri-layout-container" tri-layout @yield('container-attrs') >
+    <div refs="tri-layout@container" class="tri-layout-container" @yield('container-attrs') >
 
         <div class="tri-layout-left print-hidden pt-m" id="sidebar">
             <aside class="tri-layout-left-contents">
@@ -24,7 +34,7 @@
         </div>
 
         <div class="@yield('body-wrap-classes') tri-layout-middle">
-            <div class="tri-layout-middle-contents">
+            <div id="main-content" class="tri-layout-middle-contents">
                 @yield('body')
             </div>
         </div>
index 7fb12bd757389c0128a0e4f62f36fb03cb6e5808..5712855e6125e393c865c6f4af1fdaf94c11f5c8 100644 (file)
                                 <div role="presentation">@icon('auth/'. $driver, ['style' => 'width: 56px;height: 56px;'])</div>
                                 <div>
                                     @if($user->hasSocialAccount($driver))
-                                        <a href="{{ url("/login/service/{$driver}/detach") }}" aria-label="{{ trans('settings.users_social_disconnect') }} - {{ $driver }}"
-                                           class="button small outline">{{ trans('settings.users_social_disconnect') }}</a>
+                                        <form action="{{ url("/login/service/{$driver}/detach") }}" method="POST">
+                                            {{ csrf_field() }}
+                                            <button aria-label="{{ trans('settings.users_social_disconnect') }} - {{ $driver }}"
+                                                    class="button small outline">{{ trans('settings.users_social_disconnect') }}</button>
+                                        </form>
                                     @else
                                         <a href="{{ url("/login/service/{$driver}") }}" aria-label="{{ trans('settings.users_social_connect') }} - {{ $driver }}"
                                            class="button small outline">{{ trans('settings.users_social_connect') }}</a>
index 6bc229ec682a1fac2ae1599b4e1bfe0717c406a2..5eef511753dc16e41efb10fbff17f3dc9b742fd1 100644 (file)
@@ -9,11 +9,11 @@
 
         <main class="card content-wrap">
 
-            <div class="grid right-focus v-center">
+            <div class="flex-container-row wrap justify-space-between items-center">
                 <h1 class="list-heading">{{ trans('settings.users') }}</h1>
 
-                <div class="text-right">
-                    <div class="block inline mr-s">
+                <div>
+                    <div class="block inline mr-xs">
                         <form method="get" action="{{ url("/settings/users") }}">
                             @foreach(collect($listDetails)->except('search') as $name => $val)
                                 <input type="hidden" name="{{ $name }}" value="{{ $val }}">
index 44643d6d4cc7182a436a7f5ed0d6605d83df637f..5b724fab126dd57e6162886e6a868b0c5aeeacb6 100644 (file)
@@ -18,6 +18,7 @@ Route::delete('books/{id}', 'BookApiController@delete');
 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('books/{id}/export/markdown', 'BookExportApiController@exportMarkdown');
 
 Route::get('chapters', 'ChapterApiController@list');
 Route::post('chapters', 'ChapterApiController@create');
@@ -28,6 +29,7 @@ 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('chapters/{id}/export/markdown', 'ChapterExportApiController@exportMarkdown');
 
 Route::get('pages', 'PageApiController@list');
 Route::post('pages', 'PageApiController@create');
@@ -38,6 +40,7 @@ Route::delete('pages/{id}', 'PageApiController@delete');
 Route::get('pages/{id}/export/html', 'PageExportApiController@exportHtml');
 Route::get('pages/{id}/export/pdf', 'PageExportApiController@exportPdf');
 Route::get('pages/{id}/export/plaintext', 'PageExportApiController@exportPlainText');
+Route::get('pages/{id}/export/markdown', 'PageExportApiController@exportMarkDown');
 
 Route::get('shelves', 'BookshelfApiController@list');
 Route::post('shelves', 'BookshelfApiController@create');
index 9d482dc41a8e52bfc842eeca7a5cc364b759d44e..2bba3e2cfd074abace5d50f979ef31a77f590928 100644 (file)
@@ -14,7 +14,7 @@ Route::group(['middleware' => 'auth'], function () {
 
     // Shelves
     Route::get('/create-shelf', 'BookshelfController@create');
-    Route::group(['prefix' => 'shelves'], function() {
+    Route::group(['prefix' => 'shelves'], function () {
         Route::get('/', 'BookshelfController@index');
         Route::post('/', 'BookshelfController@store');
         Route::get('/{slug}/edit', 'BookshelfController@edit');
@@ -48,6 +48,8 @@ Route::group(['middleware' => 'auth'], function () {
         Route::put('/{bookSlug}/sort', 'BookSortController@update');
         Route::get('/{bookSlug}/export/html', 'BookExportController@html');
         Route::get('/{bookSlug}/export/pdf', 'BookExportController@pdf');
+        Route::get('/{bookSlug}/export/markdown', 'BookExportController@markdown');
+        Route::get('/{bookSlug}/export/zip', 'BookExportController@zip');
         Route::get('/{bookSlug}/export/plaintext', 'BookExportController@plainText');
 
         // Pages
@@ -58,6 +60,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/page/{pageSlug}', 'PageController@show');
         Route::get('/{bookSlug}/page/{pageSlug}/export/pdf', 'PageExportController@pdf');
         Route::get('/{bookSlug}/page/{pageSlug}/export/html', 'PageExportController@html');
+        Route::get('/{bookSlug}/page/{pageSlug}/export/markdown', 'PageExportController@markdown');
         Route::get('/{bookSlug}/page/{pageSlug}/export/plaintext', 'PageExportController@plainText');
         Route::get('/{bookSlug}/page/{pageSlug}/edit', 'PageController@edit');
         Route::get('/{bookSlug}/page/{pageSlug}/move', 'PageController@showMove');
@@ -92,6 +95,7 @@ Route::group(['middleware' => 'auth'], function () {
         Route::get('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@showPermissions');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/pdf', 'ChapterExportController@pdf');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/html', 'ChapterExportController@html');
+        Route::get('/{bookSlug}/chapter/{chapterSlug}/export/markdown', 'ChapterExportController@markdown');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/export/plaintext', 'ChapterExportController@plainText');
         Route::put('/{bookSlug}/chapter/{chapterSlug}/permissions', 'ChapterController@permissions');
         Route::get('/{bookSlug}/chapter/{chapterSlug}/delete', 'ChapterController@showDelete');
@@ -152,9 +156,15 @@ Route::group(['middleware' => 'auth'], function () {
     // User Search
     Route::get('/search/users/select', 'UserSearchController@forSelect');
 
+    // Template System
     Route::get('/templates', 'PageTemplateController@list');
     Route::get('/templates/{templateId}', 'PageTemplateController@get');
 
+    // Favourites
+    Route::get('/favourites', 'FavouriteController@index');
+    Route::post('/favourites/add', 'FavouriteController@add');
+    Route::post('/favourites/remove', 'FavouriteController@remove');
+
     // Other Pages
     Route::get('/', 'HomeController@index');
     Route::get('/home', 'HomeController@index');
@@ -220,7 +230,7 @@ Route::group(['middleware' => 'auth'], function () {
 Route::get('/login/service/{socialDriver}', 'Auth\SocialController@login');
 Route::get('/login/service/{socialDriver}/callback', 'Auth\SocialController@callback');
 Route::group(['middleware' => 'auth'], function () {
-    Route::get('/login/service/{socialDriver}/detach', 'Auth\SocialController@detach');
+    Route::post('/login/service/{socialDriver}/detach', 'Auth\SocialController@detach');
 });
 Route::get('/register/service/{socialDriver}', 'Auth\SocialController@register');
 
@@ -254,4 +264,4 @@ Route::post('/password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail
 Route::get('/password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');
 Route::post('/password/reset', 'Auth\ResetPasswordController@reset');
 
-Route::fallback('HomeController@getNotFound');
\ No newline at end of file
+Route::fallback('HomeController@getNotFound')->name('fallback');
\ No newline at end of file
index a36acdd0253bf961b0c3d44844cae14cd557f4ff..446ba28117a765a7663b6c95def30782939d9329 100644 (file)
@@ -140,4 +140,17 @@ class BooksApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $book = Book::visible()->has('pages')->has('chapters')->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$book->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $book->slug . '.md"');
+        $resp->assertSee('# ' . $book->name);
+        $resp->assertSee('# ' . $book->pages()->first()->name);
+        $resp->assertSee('# ' . $book->chapters()->first()->name);
+    }
 }
\ No newline at end of file
index c7368eaee1835209d64df5ce90642f864bf3131b..e11bb0e1cf8d4f672d71791adabae4061604b378 100644 (file)
@@ -186,4 +186,16 @@ class ChaptersApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $chapter = Chapter::visible()->has('pages')->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$chapter->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $chapter->slug . '.md"');
+        $resp->assertSee('# ' . $chapter->name);
+        $resp->assertSee('# ' . $chapter->pages()->first()->name);
+    }
 }
\ No newline at end of file
index e08e9b1b742b1424f4b6dba465ea994395d80908..9fab675e739644b11df8284a0355c6456627e6fb 100644 (file)
@@ -258,4 +258,15 @@ class PagesApiTest extends TestCase
         $resp->assertStatus(200);
         $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.pdf"');
     }
+
+    public function test_export_markdown_endpoint()
+    {
+        $this->actingAsApiEditor();
+        $page = Page::visible()->first();
+
+        $resp = $this->get($this->baseEndpoint . "/{$page->id}/export/markdown");
+        $resp->assertStatus(200);
+        $resp->assertSee('# ' . $page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"');
+    }
 }
\ No newline at end of file
index 840dfd630eeeea91c1cf3a8dd9e98b6d7aa6fb97..60859e324743c059a38ccd69235b85ac11533739 100644 (file)
@@ -4,13 +4,12 @@ use BookStack\Auth\Access\LdapService;
 use BookStack\Auth\Role;
 use BookStack\Auth\Access\Ldap;
 use BookStack\Auth\User;
-use BookStack\Exceptions\LdapException;
 use Mockery\MockInterface;
-use Tests\BrowserKitTest;
+use Tests\TestCase;
+use Tests\TestResponse;
 
-class LdapTest extends BrowserKitTest
+class LdapTest extends TestCase
 {
-
     /**
      * @var MockInterface
      */
@@ -35,6 +34,7 @@ class LdapTest extends BrowserKitTest
             'services.ldap.user_filter' => '(&(uid=${user}))',
             'services.ldap.follow_referrals' => false,
             'services.ldap.tls_insecure' => false,
+            'services.ldap.thumbnail_attribute' => null,
         ]);
         $this->mockLdap = \Mockery::mock(Ldap::class);
         $this->app[Ldap::class] = $this->mockLdap;
@@ -51,25 +51,24 @@ class LdapTest extends BrowserKitTest
 
     protected function mockEscapes($times = 1)
     {
-        $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function($val) {
+        $this->mockLdap->shouldReceive('escape')->times($times)->andReturnUsing(function ($val) {
             return ldap_escape($val);
         });
     }
 
     protected function mockExplodes($times = 1)
     {
-        $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function($dn, $withAttrib) {
+        $this->mockLdap->shouldReceive('explodeDn')->times($times)->andReturnUsing(function ($dn, $withAttrib) {
             return ldap_explode_dn($dn, $withAttrib);
         });
     }
 
-    protected function mockUserLogin()
+    protected function mockUserLogin(?string $email = null): TestResponse
     {
-        return $this->visit('/login')
-            ->see('Username')
-            ->type($this->mockUser->name, '#username')
-            ->type($this->mockUser->password, '#password')
-            ->press('Log In');
+        return $this->post('/login', [
+            'username' => $this->mockUser->name,
+            'password' => $this->mockUser->password,
+        ] + ($email ? ['email' => $email] : []));
     }
 
     /**
@@ -96,14 +95,20 @@ class LdapTest extends BrowserKitTest
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')->see('Please enter an email to use for this account.');
-
-        $this->type($this->mockUser->email, '#email')
-            ->press('Log In')
-            ->seePageIs('/')
-            ->see($this->mockUser->name)
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name]);
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/login');
+        $resp = $this->followRedirects($resp);
+        $resp->assertSee('Please enter an email to use for this account.');
+        $resp->assertSee($this->mockUser->name);
+
+        $resp = $this->followingRedirects()->mockUserLogin($this->mockUser->email);
+        $resp->assertElementExists('#home-default');
+        $resp->assertSee($this->mockUser->name);
+        $this->assertDatabaseHas('users', [
+            'email' => $this->mockUser->email,
+            'email_confirmed' => false,
+            'external_auth_id' => $this->mockUser->name
+        ]);
     }
 
     public function test_email_domain_restriction_active_on_new_ldap_login()
@@ -121,17 +126,17 @@ class LdapTest extends BrowserKitTest
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')
-            ->see('Please enter an email to use for this account.');
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/login');
+        $this->followRedirects($resp)->assertSee('Please enter an email to use for this account.');
+
 
         $email = '[email protected]';
+        $resp = $this->mockUserLogin($email);
+        $resp->assertRedirect('/login');
+        $this->followRedirects($resp)->assertSee('That email domain does not have access to this application');
 
-        $this->type($email, '#email')
-            ->press('Log In')
-            ->seePageIs('/login')
-            ->see('That email domain does not have access to this application')
-            ->dontSeeInDatabase('users', ['email' => $email]);
+        $this->assertDatabaseMissing('users', ['email' => $email]);
     }
 
     public function test_login_works_when_no_uid_provided_by_ldap_server()
@@ -147,10 +152,10 @@ class LdapTest extends BrowserKitTest
                 'mail' => [$this->mockUser->email]
             ]]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/')
-            ->see($this->mockUser->name)
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/');
+        $this->followRedirects($resp)->assertSee($this->mockUser->name);
+        $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $ldapDn]);
     }
 
     public function test_a_custom_uid_attribute_can_be_specified_and_is_used_properly()
@@ -169,10 +174,10 @@ class LdapTest extends BrowserKitTest
             ]]);
 
 
-        $this->mockUserLogin()
-            ->seePageIs('/')
-            ->see($this->mockUser->name)
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/');
+        $this->followRedirects($resp)->assertSee($this->mockUser->name);
+        $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
     }
 
     public function test_initial_incorrect_credentials()
@@ -187,9 +192,10 @@ class LdapTest extends BrowserKitTest
             ]]);
         $this->mockLdap->shouldReceive('bind')->times(2)->andReturn(true, false);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')->see('These credentials do not match our records.')
-            ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/login');
+        $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
+        $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
     }
 
     public function test_login_not_found_username()
@@ -199,49 +205,59 @@ class LdapTest extends BrowserKitTest
             ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
             ->andReturn(['count' => 0]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')->see('These credentials do not match our records.')
-            ->dontSeeInDatabase('users', ['external_auth_id' => $this->mockUser->name]);
+        $resp = $this->mockUserLogin();
+        $resp->assertRedirect('/login');
+        $this->followRedirects($resp)->assertSee('These credentials do not match our records.');
+        $this->assertDatabaseMissing('users', ['external_auth_id' => $this->mockUser->name]);
     }
 
-
     public function test_create_user_form()
     {
-        $this->asAdmin()->visit('/settings/users/create')
-            ->dontSee('Password')
-            ->type($this->mockUser->name, '#name')
-            ->type($this->mockUser->email, '#email')
-            ->press('Save')
-            ->see('The external auth id field is required.')
-            ->type($this->mockUser->name, '#external_auth_id')
-            ->press('Save')
-            ->seePageIs('/settings/users')
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
+        $userForm = $this->asAdmin()->get('/settings/users/create');
+        $userForm->assertDontSee('Password');
+
+        $save = $this->post('/settings/users/create', [
+            'name' => $this->mockUser->name,
+            'email' => $this->mockUser->email,
+        ]);
+        $save->assertSessionHasErrors(['external_auth_id' => 'The external auth id field is required.']);
+
+        $save = $this->post('/settings/users/create', [
+            'name' => $this->mockUser->name,
+            'email' => $this->mockUser->email,
+            'external_auth_id' => $this->mockUser->name,
+        ]);
+        $save->assertRedirect('/settings/users');
+        $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'external_auth_id' => $this->mockUser->name, 'email_confirmed' => true]);
     }
 
     public function test_user_edit_form()
     {
         $editUser = $this->getNormalUser();
-        $this->asAdmin()->visit('/settings/users/' . $editUser->id)
-            ->see('Edit User')
-            ->dontSee('Password')
-            ->type('test_auth_id', '#external_auth_id')
-            ->press('Save')
-            ->seePageIs('/settings/users')
-            ->seeInDatabase('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
+        $editPage = $this->asAdmin()->get("/settings/users/{$editUser->id}");
+        $editPage->assertSee('Edit User');
+        $editPage->assertDontSee('Password');
+
+        $update = $this->put("/settings/users/{$editUser->id}", [
+            'name' => $editUser->name,
+            'email' => $editUser->email,
+            'external_auth_id' => 'test_auth_id',
+        ]);
+        $update->assertRedirect('/settings/users');
+        $this->assertDatabaseHas('users', ['email' => $editUser->email, 'external_auth_id' => 'test_auth_id']);
     }
 
     public function test_registration_disabled()
     {
-        $this->visit('/register')
-            ->seePageIs('/login');
+        $this->followingRedirects()->get('/register')->assertElementContains('#content', 'Log In');
     }
 
     public function test_non_admins_cannot_change_auth_id()
     {
         $testUser = $this->getNormalUser();
-        $this->actingAs($testUser)->visit('/settings/users/' . $testUser->id)
-            ->dontSee('External Authentication');
+        $this->actingAs($testUser)
+            ->get('/settings/users/' . $testUser->id)
+            ->assertDontSee('External Authentication');
     }
 
     public function test_login_maps_roles_and_retains_existing_roles()
@@ -273,18 +289,18 @@ class LdapTest extends BrowserKitTest
                 ]
             ]]);
 
-        $this->mockUserLogin()->seePageIs('/');
+        $this->mockUserLogin()->assertRedirect('/');
 
         $user = User::where('email', $this->mockUser->email)->first();
-        $this->seeInDatabase('role_user', [
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive->id
         ]);
-        $this->seeInDatabase('role_user', [
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive2->id
         ]);
-        $this->seeInDatabase('role_user', [
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $existingRole->id
         ]);
@@ -317,14 +333,14 @@ class LdapTest extends BrowserKitTest
                 ]
             ]]);
 
-        $this->mockUserLogin()->seePageIs('/');
+        $this->mockUserLogin()->assertRedirect('/');
 
-        $user = User::where('email', $this->mockUser->email)->first();
-        $this->seeInDatabase('role_user', [
+        $user = User::query()->where('email', $this->mockUser->email)->first();
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive->id
         ]);
-        $this->dontSeeInDatabase('role_user', [
+        $this->assertDatabaseMissing('role_user', [
             'user_id' => $user->id,
             'role_id' => $existingRole->id
         ]);
@@ -333,8 +349,8 @@ class LdapTest extends BrowserKitTest
     public function test_external_auth_id_visible_in_roles_page_when_ldap_active()
     {
         $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');
+        $this->asAdmin()->get('/settings/roles/' . $role->id)
+            ->assertSee('ex-auth-a');
     }
 
     public function test_login_maps_roles_using_external_auth_ids_if_set()
@@ -362,14 +378,14 @@ class LdapTest extends BrowserKitTest
                 ]
             ]]);
 
-        $this->mockUserLogin()->seePageIs('/');
+        $this->mockUserLogin()->assertRedirect('/');
 
-        $user = User::where('email', $this->mockUser->email)->first();
-        $this->seeInDatabase('role_user', [
+        $user = User::query()->where('email', $this->mockUser->email)->first();
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive->id
         ]);
-        $this->dontSeeInDatabase('role_user', [
+        $this->assertDatabaseMissing('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToNotReceive->id
         ]);
@@ -404,14 +420,14 @@ class LdapTest extends BrowserKitTest
                 ]
             ]]);
 
-        $this->mockUserLogin()->seePageIs('/');
+        $this->mockUserLogin()->assertRedirect('/');
 
-        $user = User::where('email', $this->mockUser->email)->first();
-        $this->seeInDatabase('role_user', [
+        $user = User::query()->where('email', $this->mockUser->email)->first();
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive->id
         ]);
-        $this->seeInDatabase('role_user', [
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive2->id
         ]);
@@ -433,14 +449,13 @@ class LdapTest extends BrowserKitTest
                 'displayname' => 'displayNameAttribute'
             ]]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')->see('Please enter an email to use for this account.');
+        $this->mockUserLogin()->assertRedirect('/login');
+        $this->get('/login')->assertSee('Please enter an email to use for this account.');
 
-        $this->type($this->mockUser->email, '#email')
-            ->press('Log In')
-            ->seePageIs('/')
-            ->see('displayNameAttribute')
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
+        $resp = $this->mockUserLogin($this->mockUser->email);
+        $resp->assertRedirect('/');
+        $this->get('/')->assertSee('displayNameAttribute');
+        $this->assertDatabaseHas('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => 'displayNameAttribute']);
     }
 
     public function test_login_uses_default_display_name_attribute_if_specified_not_present()
@@ -458,14 +473,18 @@ class LdapTest extends BrowserKitTest
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
 
-        $this->mockUserLogin()
-            ->seePageIs('/login')->see('Please enter an email to use for this account.');
+        $this->mockUserLogin()->assertRedirect('/login');
+        $this->get('/login')->assertSee('Please enter an email to use for this account.');
 
-        $this->type($this->mockUser->email, '#email')
-            ->press('Log In')
-            ->seePageIs('/')
-            ->see($this->mockUser->name)
-            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => $this->mockUser->name, 'name' => $this->mockUser->name]);
+        $resp = $this->mockUserLogin($this->mockUser->email);
+        $resp->assertRedirect('/');
+        $this->get('/')->assertSee($this->mockUser->name);
+        $this->assertDatabaseHas('users', [
+            'email' => $this->mockUser->email,
+            'email_confirmed' => false,
+            'external_auth_id' => $this->mockUser->name,
+            'name' => $this->mockUser->name
+        ]);
     }
 
     protected function checkLdapReceivesCorrectDetails($serverString, $expectedHost, $expectedPort)
@@ -549,11 +568,11 @@ class LdapTest extends BrowserKitTest
                 'dn' => ['dc=test' . config('services.ldap.base_dn')]
             ]]);
 
-        $this->post('/login', [
+        $resp = $this->post('/login', [
             'username' => $this->mockUser->name,
             'password' => $this->mockUser->password,
         ]);
-        $this->seeJsonStructure([
+        $resp->assertJsonStructure([
             'details_from_ldap' => [],
             'details_bookstack_parsed' => [],
         ]);
@@ -571,8 +590,8 @@ class LdapTest extends BrowserKitTest
         config()->set(['services.ldap.start_tls' => true]);
         $this->mockLdap->shouldReceive('startTls')->once()->andReturn(false);
         $this->commonLdapMocks(1, 1, 0, 0, 0);
-        $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
-        $this->assertResponseStatus(500);
+        $resp = $this->post('/login', ['username' => 'timmyjenkins', 'password' => 'cattreedog']);
+        $resp->assertStatus(500);
     }
 
     public function test_ldap_attributes_can_be_binary_decoded_if_marked()
@@ -610,13 +629,12 @@ class LdapTest extends BrowserKitTest
             ]]);
 
         // First user login
-        $this->mockUserLogin()->seePageIs('/');
+        $this->mockUserLogin()->assertRedirect('/');
 
         // Second user login
         auth()->logout();
-        $this->post('/login', ['username' => 'bscott', 'password' => 'pass'])->followRedirects();
-
-        $this->see('A user with the email [email protected] already exists but with different credentials');
+        $resp = $this->followingRedirects()->post('/login', ['username' => 'bscott', 'password' => 'pass']);
+        $resp->assertSee('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()
@@ -645,20 +663,20 @@ class LdapTest extends BrowserKitTest
                 ]
             ]]);
 
-        $this->mockUserLogin()->seePageIs('/register/confirm');
-        $this->seeInDatabase('users', [
+        $this->followingRedirects()->mockUserLogin()->assertSee('Thanks for registering!');
+        $this->assertDatabaseHas('users', [
             'email' => $user->email,
             'email_confirmed' => false,
         ]);
 
-        $user  = User::query()->where('email', '=', $user->email)->first();
-        $this->seeInDatabase('role_user', [
+        $user = User::query()->where('email', '=', $user->email)->first();
+        $this->assertDatabaseHas('role_user', [
             'user_id' => $user->id,
             'role_id' => $roleToReceive->id
         ]);
 
         $homePage = $this->get('/');
-        $homePage->assertRedirectedTo('/register/confirm/awaiting');
+        $homePage->assertRedirect('/register/confirm/awaiting');
     }
 
     public function test_failed_logins_are_logged_when_message_configured()
@@ -668,4 +686,28 @@ class LdapTest extends BrowserKitTest
         $this->runFailedAuthLogin();
         $this->assertTrue($log->hasWarningThatContains('Failed login for timmyjenkins'));
     }
+
+    public function test_thumbnail_attribute_used_as_user_avatar_if_configured()
+    {
+        config()->set(['services.ldap.thumbnail_attribute' => 'jpegPhoto']);
+
+        $this->commonLdapMocks(1, 1, 1, 2, 1);
+        $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(1)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'cn' => [$this->mockUser->name],
+                'dn' => $ldapDn,
+                'jpegphoto' => [base64_decode('/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8Q
+EBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=')],
+                'mail' => [$this->mockUser->email]
+            ]]);
+
+        $this->mockUserLogin()
+            ->assertRedirect('/');
+
+        $user = User::query()->where('email', '=', $this->mockUser->email)->first();
+        $this->assertNotNull($user->avatar);
+        $this->assertEquals('8c90748342f19b195b9c6b4eff742ded', md5_file(public_path($user->avatar->path)));
+    }
 }
index 58c02b47188a625b83a5aeb3d0ad80c5f4f81de0..b6b02e2f76d8aef7a65493bfda7449b8ca5271dd 100644 (file)
@@ -28,6 +28,7 @@ class Saml2Test extends TestCase
             'saml2.autoload_from_metadata' => false,
             'saml2.onelogin.idp.x509cert' => $this->testCert,
             'saml2.onelogin.debug' => false,
+            'saml2.onelogin.security.requestedAuthnContext' => true,
         ]);
     }
 
@@ -328,6 +329,40 @@ class Saml2Test extends TestCase
         });
     }
 
+    public function test_login_request_contains_expected_default_authncontext()
+    {
+        $authReq = $this->getAuthnRequest();
+        $this->assertStringContainsString('samlp:RequestedAuthnContext Comparison="exact"', $authReq);
+        $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml:AuthnContextClassRef>', $authReq);
+    }
+
+    public function test_false_idp_authncontext_option_does_not_pass_authncontext_in_saml_request()
+    {
+        config()->set(['saml2.onelogin.security.requestedAuthnContext' => false]);
+        $authReq = $this->getAuthnRequest();
+        $this->assertStringNotContainsString('samlp:RequestedAuthnContext', $authReq);
+        $this->assertStringNotContainsString('<saml:AuthnContextClassRef>', $authReq);
+    }
+
+    public function test_array_idp_authncontext_option_passes_value_as_authncontextclassref_in_request()
+    {
+        config()->set(['saml2.onelogin.security.requestedAuthnContext' => ['urn:federation:authentication:windows', 'urn:federation:authentication:linux']]);
+        $authReq = $this->getAuthnRequest();
+        $this->assertStringContainsString('samlp:RequestedAuthnContext', $authReq);
+        $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:federation:authentication:windows</saml:AuthnContextClassRef>', $authReq);
+        $this->assertStringContainsString('<saml:AuthnContextClassRef>urn:federation:authentication:linux</saml:AuthnContextClassRef>', $authReq);
+    }
+
+    protected function getAuthnRequest(): string
+    {
+        $req = $this->post('/saml2/login');
+        $location = $req->headers->get('Location');
+        $query = explode('?', $location)[1];
+        $params = [];
+        parse_str($query, $params);
+        return gzinflate(base64_decode($params['SAMLRequest']));
+    }
+
     protected function withGet(array $options, callable $callback)
     {
         return $this->withGlobal($_GET, $options, $callback);
index 4369d8b7abca6faa84780f73c13bdd4176176aac..60de8fbcbbcc07165081dbbfe7e6f8bd98e26740 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Auth;
 
+use BookStack\Auth\SocialAccount;
 use BookStack\Auth\User;
 use DB;
 use Laravel\Socialite\Contracts\Factory;
@@ -83,6 +84,31 @@ class SocialAuthTest extends TestCase
         $resp->assertDontSee("login-form");
     }
 
+    public function test_social_account_detach()
+    {
+        $editor = $this->getEditor();
+        config([
+            'GITHUB_APP_ID' => 'abc123', 'GITHUB_APP_SECRET' => '123abc',
+            'APP_URL' => 'http://localhost'
+        ]);
+
+        $socialAccount = SocialAccount::query()->forceCreate([
+            'user_id' => $editor->id,
+            'driver' => 'github',
+            'driver_id' => 'logintest123',
+        ]);
+
+        $resp = $this->actingAs($editor)->get($editor->getEditUrl());
+        $resp->assertElementContains('form[action$="/login/service/github/detach"]', 'Disconnect Account');
+
+        $resp = $this->post('/login/service/github/detach');
+        $resp->assertRedirect($editor->getEditUrl());
+        $resp = $this->followRedirects($resp);
+        $resp->assertSee('Github account was successfully disconnected from your profile.');
+
+        $this->assertDatabaseMissing('social_accounts', ['id' => $socialAccount->id]);
+    }
+
     public function test_social_autoregister()
     {
         config([
index 45e20b5e284aad7875b9b8e05abe534efea523de..7917a0c409e881a7b8790c9a7c19584c5f1b0327 100644 (file)
@@ -47,14 +47,6 @@ abstract class BrowserKitTest extends TestCase
     }
 
 
-    /**
-     * Get a user that's not a system user such as the guest user.
-     */
-    public function getNormalUser()
-    {
-        return User::where('system_name', '=', null)->get()->last();
-    }
-
     /**
      * Quickly sets an array of settings.
      * @param $settingsArray
index 6c2cf30d416f9126880a3611b2d6dcd039f3cab5..e1b06431b43a2edda49d828ede935fd9392da66f 100644 (file)
@@ -31,4 +31,19 @@ class BookTest extends TestCase
         $redirectReq = $this->get($deleteReq->baseResponse->headers->get('location'));
         $redirectReq->assertNotificationContains('Book Successfully Deleted');
     }
+
+    public function test_next_previous_navigation_controls_show_within_book_content()
+    {
+        $book = Book::query()->first();
+        $chapter = $book->chapters->first();
+
+        $resp = $this->asEditor()->get($chapter->getUrl());
+        $resp->assertElementContains('#sibling-navigation', 'Next');
+        $resp->assertElementContains('#sibling-navigation', substr($chapter->pages[0]->name, 0, 20));
+
+        $resp = $this->get($chapter->pages[0]->getUrl());
+        $resp->assertElementContains('#sibling-navigation', substr($chapter->pages[1]->name, 0, 20));
+        $resp->assertElementContains('#sibling-navigation', 'Previous');
+        $resp->assertElementContains('#sibling-navigation', substr($chapter->name, 0, 20));
+    }
 }
\ No newline at end of file
index 49ceede9f3edd23207b5f62906c14fe677fcd043..5ab6ad9a81beb34f1620e019f4bfe8dbb6c56ea9 100644 (file)
@@ -1,35 +1,33 @@
 <?php namespace Tests\Entity;
 
 use BookStack\Entities\Models\Page;
-use Tests\BrowserKitTest;
+use Tests\TestCase;
 
-class CommentSettingTest extends BrowserKitTest
+class CommentSettingTest extends TestCase
 {
     protected $page;
 
     public function setUp(): void
     {
         parent::setUp();
-        $this->page = Page::first();
+        $this->page = Page::query()->first();
     }
 
     public function test_comment_disable()
     {
-        $this->asAdmin();
-
         $this->setSettings(['app-disable-comments' => 'true']);
+        $this->asAdmin();
 
-        $this->asAdmin()->visit($this->page->getUrl())
-            ->pageNotHasElement('.comments-list');
+        $this->asAdmin()->get($this->page->getUrl())
+            ->assertElementNotExists('.comments-list');
     }
 
     public function test_comment_enable()
     {
-        $this->asAdmin();
-
         $this->setSettings(['app-disable-comments' => 'false']);
+        $this->asAdmin();
 
-        $this->asAdmin()->visit($this->page->getUrl())
-            ->pageHasElement('.comments-list');
+        $this->asAdmin()->get($this->page->getUrl())
+            ->assertElementExists('.comments-list');
     }
 }
\ No newline at end of file
index 05672c6ca47b09252ea8ff87bf00b190c5a20b5c..f437904e842517375b20599142a538f07ebfe0ab 100644 (file)
@@ -1,5 +1,6 @@
 <?php namespace Tests\Entity;
 
+use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Models\Page;
 use Illuminate\Support\Facades\Storage;
@@ -11,7 +12,7 @@ class ExportTest extends TestCase
 
     public function test_page_text_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asEditor();
 
         $resp = $this->get($page->getUrl('/export/plaintext'));
@@ -22,7 +23,7 @@ class ExportTest extends TestCase
 
     public function test_page_pdf_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asEditor();
 
         $resp = $this->get($page->getUrl('/export/pdf'));
@@ -32,7 +33,7 @@ class ExportTest extends TestCase
 
     public function test_page_html_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asEditor();
 
         $resp = $this->get($page->getUrl('/export/html'));
@@ -43,7 +44,7 @@ class ExportTest extends TestCase
 
     public function test_book_text_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $book = $page->book;
         $this->asEditor();
 
@@ -56,7 +57,7 @@ class ExportTest extends TestCase
 
     public function test_book_pdf_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $book = $page->book;
         $this->asEditor();
 
@@ -67,7 +68,7 @@ class ExportTest extends TestCase
 
     public function test_book_html_export()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $book = $page->book;
         $this->asEditor();
 
@@ -94,7 +95,7 @@ class ExportTest extends TestCase
 
     public function test_chapter_text_export()
     {
-        $chapter = Chapter::first();
+        $chapter = Chapter::query()->first();
         $page = $chapter->pages[0];
         $this->asEditor();
 
@@ -107,7 +108,7 @@ class ExportTest extends TestCase
 
     public function test_chapter_pdf_export()
     {
-        $chapter = Chapter::first();
+        $chapter = Chapter::query()->first();
         $this->asEditor();
 
         $resp = $this->get($chapter->getUrl('/export/pdf'));
@@ -117,7 +118,7 @@ class ExportTest extends TestCase
 
     public function test_chapter_html_export()
     {
-        $chapter = Chapter::first();
+        $chapter = Chapter::query()->first();
         $page = $chapter->pages[0];
         $this->asEditor();
 
@@ -130,7 +131,7 @@ class ExportTest extends TestCase
 
     public function test_page_html_export_contains_custom_head_if_set()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
 
         $customHeadContent = "<style>p{color: red;}</style>";
         $this->setSettings(['app-custom-head' => $customHeadContent]);
@@ -139,20 +140,32 @@ class ExportTest extends TestCase
         $resp->assertSee($customHeadContent);
     }
 
+    public function test_page_html_export_does_not_break_with_only_comments_in_custom_head()
+    {
+        $page = Page::query()->first();
+
+        $customHeadContent = "<!-- A comment -->";
+        $this->setSettings(['app-custom-head' => $customHeadContent]);
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertSee($customHeadContent);
+    }
+
     public function test_page_html_export_use_absolute_dates()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
 
         $resp = $this->asEditor()->get($page->getUrl('/export/html'));
-        $resp->assertSee($page->created_at->toDayDateTimeString());
+        $resp->assertSee($page->created_at->formatLocalized('%e %B %Y %H:%M:%S'));
         $resp->assertDontSee($page->created_at->diffForHumans());
-        $resp->assertSee($page->updated_at->toDayDateTimeString());
+        $resp->assertSee($page->updated_at->formatLocalized('%e %B %Y %H:%M:%S'));
         $resp->assertDontSee($page->updated_at->diffForHumans());
     }
 
     public function test_page_export_does_not_include_user_or_revision_links()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
 
         $resp = $this->asEditor()->get($page->getUrl('/export/html'));
         $resp->assertDontSee($page->getUrl('/revisions'));
@@ -162,7 +175,7 @@ class ExportTest extends TestCase
 
     public function test_page_export_sets_right_data_type_for_svg_embeds()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         Storage::disk('local')->makeDirectory('uploads/images/gallery');
         Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
         $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg">';
@@ -178,7 +191,7 @@ class ExportTest extends TestCase
 
     public function test_page_image_containment_works_on_multiple_images_within_a_single_line()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         Storage::disk('local')->makeDirectory('uploads/images/gallery');
         Storage::disk('local')->put('uploads/images/gallery/svg_test.svg', '<svg></svg>');
         Storage::disk('local')->put('uploads/images/gallery/svg_test2.svg', '<svg></svg>');
@@ -194,7 +207,7 @@ class ExportTest extends TestCase
 
     public function test_page_export_contained_html_image_fetches_only_run_when_url_points_to_image_upload_folder()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $page->html = '<img src="http://localhost/uploads/images/gallery/svg_test.svg"/>'
             .'<img src="http://localhost/uploads/svg_test.svg"/>'
             .'<img src="/uploads/svg_test.svg"/>';
@@ -214,4 +227,117 @@ class ExportTest extends TestCase
         $resp->assertSee('src="/uploads/svg_test.svg"');
     }
 
+    public function test_exports_removes_scripts_from_custom_head()
+    {
+        $entities = [
+            Page::query()->first(), Chapter::query()->first(), Book::query()->first(),
+        ];
+        setting()->put('app-custom-head', '<script>window.donkey = "cat";</script><style>.my-test-class { color: red; }</style>');
+
+        foreach ($entities as $entity) {
+            $resp = $this->asEditor()->get($entity->getUrl('/export/html'));
+            $resp->assertDontSee('window.donkey');
+            $resp->assertDontSee('script');
+            $resp->assertSee('.my-test-class { color: red; }');
+        }
+    }
+
+    public function test_page_export_with_deleted_creator_and_updater()
+    {
+        $user = $this->getViewer(['name' => 'ExportWizardTheFifth']);
+        $page = Page::query()->first();
+        $page->created_by = $user->id;
+        $page->updated_by = $user->id;
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/html'));
+        $resp->assertSee('ExportWizardTheFifth');
+
+        $user->delete();
+        $resp = $this->get($page->getUrl('/export/html'));
+        $resp->assertStatus(200);
+        $resp->assertDontSee('ExportWizardTheFifth');
+    }
+
+    public function test_page_markdown_export()
+    {
+        $page = Page::query()->first();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
+        $resp->assertStatus(200);
+        $resp->assertSee($page->name);
+        $resp->assertHeader('Content-Disposition', 'attachment; filename="' . $page->slug . '.md"');
+    }
+
+    public function test_page_markdown_export_uses_existing_markdown_if_apparent()
+    {
+        $page = Page::query()->first()->forceFill([
+            'markdown' => '# A header',
+            'html' => '<h1>Dogcat</h1>',
+        ]);
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
+        $resp->assertSee('A header');
+        $resp->assertDontSee('Dogcat');
+    }
+
+    public function test_page_markdown_export_converts_html_where_no_markdown()
+    {
+        $page = Page::query()->first()->forceFill([
+            'markdown' => '',
+            'html' => "<h1>Dogcat</h1><p>Some <strong>bold</strong> text</p>",
+        ]);
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
+        $resp->assertSee("# Dogcat\n\nSome **bold** text");
+    }
+
+    public function test_page_markdown_export_does_not_convert_callouts()
+    {
+        $page = Page::query()->first()->forceFill([
+            'markdown' => '',
+            'html' => "<h1>Dogcat</h1><p class=\"callout info\">Some callout text</p><p>Another line</p>",
+        ]);
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
+        $resp->assertSee("# Dogcat\n\n<p class=\"callout info\">Some callout text</p>\n\nAnother line");
+    }
+
+    public function test_page_markdown_export_handles_bookstacks_wysiwyg_codeblock_format()
+    {
+        $page = Page::query()->first()->forceFill([
+            'markdown' => '',
+            'html' => '<h1>Dogcat</h1>'."\r\n".'<pre id="bkmrk-var-a-%3D-%27cat%27%3B"><code class="language-JavaScript">var a = \'cat\';</code></pre><p>Another line</p>',
+        ]);
+        $page->save();
+
+        $resp = $this->asEditor()->get($page->getUrl('/export/markdown'));
+        $resp->assertSee("# Dogcat\n\n```JavaScript\nvar a = 'cat';\n```\n\nAnother line");
+    }
+
+    public function test_chapter_markdown_export()
+    {
+        $chapter = Chapter::query()->first();
+        $page = $chapter->pages()->first();
+        $resp = $this->asEditor()->get($chapter->getUrl('/export/markdown'));
+
+        $resp->assertSee('# ' . $chapter->name);
+        $resp->assertSee('# ' . $page->name);
+    }
+
+    public function test_book_markdown_export()
+    {
+        $book = Book::query()->whereHas('pages')->whereHas('chapters')->first();
+        $chapter = $book->chapters()->first();
+        $page = $chapter->pages()->first();
+        $resp = $this->asEditor()->get($book->getUrl('/export/markdown'));
+
+        $resp->assertSee('# ' . $book->name);
+        $resp->assertSee('# ' . $chapter->name);
+        $resp->assertSee('# ' . $page->name);
+    }
+
 }
index 6d5200794bc79354bd8243d80711867323f9f0c6..f1462dbd08a61ecd07770a47d612e546368b3ddf 100644 (file)
@@ -3,14 +3,18 @@
 use BookStack\Entities\Tools\PageContent;
 use BookStack\Entities\Models\Page;
 use Tests\TestCase;
+use Tests\Uploads\UsesImages;
 
 class PageContentTest extends TestCase
 {
+    use UsesImages;
+
+    protected $base64Jpeg = '/9j/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/yQALCAABAAEBAREA/8wABgAQEAX/2gAIAQEAAD8A0s8g/9k=';
 
     public function test_page_includes()
     {
-        $page = Page::first();
-        $secondPage = Page::where('id', '!=', $page->id)->first();
+        $page = Page::query()->first();
+        $secondPage = Page::query()->where('id', '!=', $page->id)->first();
 
         $secondPage->html = "<p id='section1'>Hello, This is a test</p><p id='section2'>This is a second block of content</p>";
         $secondPage->save();
@@ -38,8 +42,8 @@ class PageContentTest extends TestCase
 
     public function test_saving_page_with_includes()
     {
-        $page = Page::first();
-        $secondPage = Page::where('id', '!=', $page->id)->first();
+        $page = Page::query()->first();
+        $secondPage = Page::query()->where('id', '!=', $page->id)->first();
 
         $this->asEditor();
         $includeTag = '{{@' . $secondPage->id . '}}';
@@ -56,8 +60,8 @@ class PageContentTest extends TestCase
 
     public function test_page_includes_do_not_break_tables()
     {
-        $page = Page::first();
-        $secondPage = Page::where('id', '!=', $page->id)->first();
+        $page = Page::query()->first();
+        $secondPage = Page::query()->where('id', '!=', $page->id)->first();
 
         $content = '<table id="table"><tbody><tr><td>test</td></tr></tbody></table>';
         $secondPage->html = $content;
@@ -93,7 +97,7 @@ class PageContentTest extends TestCase
     public function test_page_content_scripts_removed_by_default()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
         $script = 'abc123<script>console.log("hello-test")</script>abc123';
         $page->html = "escape {$script}";
         $page->save();
@@ -116,7 +120,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -141,7 +145,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -167,7 +171,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -188,7 +192,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -211,7 +215,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -228,7 +232,7 @@ class PageContentTest extends TestCase
     public function test_page_inline_on_attributes_removed_by_default()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
         $script = '<p onmouseenter="console.log(\'test\')">Hello</p>';
         $page->html = "escape {$script}";
         $page->save();
@@ -251,7 +255,7 @@ class PageContentTest extends TestCase
         ];
 
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         foreach ($checks as $check) {
             $page->html = $check;
@@ -267,7 +271,7 @@ class PageContentTest extends TestCase
     public function test_page_content_scripts_show_when_configured()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
         config()->push('app.allow_content_scripts', 'true');
 
         $script = 'abc123<script>console.log("hello-test")</script>abc123';
@@ -282,7 +286,7 @@ class PageContentTest extends TestCase
     public function test_page_inline_on_attributes_show_if_configured()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
         config()->push('app.allow_content_scripts', 'true');
 
         $script = '<p onmouseenter="console.log(\'test\')">Hello</p>';
@@ -297,7 +301,7 @@ class PageContentTest extends TestCase
     public function test_duplicate_ids_does_not_break_page_render()
     {
         $this->asEditor();
-        $pageA = Page::first();
+        $pageA = Page::query()->first();
         $pageB = Page::query()->where('id', '!=', $pageA->id)->first();
 
         $content = '<ul id="bkmrk-xxx-%28"></ul> <ul id="bkmrk-xxx-%28"></ul>';
@@ -314,7 +318,7 @@ class PageContentTest extends TestCase
     public function test_duplicate_ids_fixed_on_page_save()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         $content = '<ul id="bkmrk-test"><li>test a</li><li><ul id="bkmrk-test"><li>test b</li></ul></li></ul>';
         $pageSave = $this->put($page->getUrl(), [
@@ -324,14 +328,14 @@ class PageContentTest extends TestCase
         ]);
         $pageSave->assertRedirect();
 
-        $updatedPage = Page::where('id', '=', $page->id)->first();
+        $updatedPage = Page::query()->where('id', '=', $page->id)->first();
         $this->assertEquals(substr_count($updatedPage->html, "bkmrk-test\""), 1);
     }
 
     public function test_anchors_referencing_non_bkmrk_ids_rewritten_after_save()
     {
         $this->asEditor();
-        $page = Page::first();
+        $page = Page::query()->first();
 
         $content = '<h1 id="non-standard-id">test</h1><p><a href="#non-standard-id">link</a></p>';
         $this->put($page->getUrl(), [
@@ -340,7 +344,7 @@ class PageContentTest extends TestCase
             'summary' => ''
         ]);
 
-        $updatedPage = Page::where('id', '=', $page->id)->first();
+        $updatedPage = Page::query()->where('id', '=', $page->id)->first();
         $this->assertStringContainsString('id="bkmrk-test"', $updatedPage->html);
         $this->assertStringContainsString('href="#bkmrk-test"', $updatedPage->html);
     }
@@ -479,4 +483,83 @@ class PageContentTest extends TestCase
         $pageView = $this->get($page->getUrl());
         $pageView->assertElementExists('.page-content p > s');
     }
+
+    public function test_page_markdown_single_html_comment_saving()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $content = '<!-- Test Comment -->';
+        $this->put($page->getUrl(), [
+            'name' => $page->name,  'markdown' => $content,
+            'html' => '', 'summary' => ''
+        ]);
+
+        $page->refresh();
+        $this->assertStringMatchesFormat($content, $page->html);
+
+        $pageView = $this->get($page->getUrl());
+        $pageView->assertStatus(200);
+        $pageView->assertSee($content);
+    }
+
+    public function test_base64_images_get_extracted_from_page_content()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $this->put($page->getUrl(), [
+            'name' => $page->name, 'summary' => '',
+            'html' => '<p>test<img src="data:image/jpeg;base64,'.$this->base64Jpeg.'"/></p>',
+        ]);
+
+        $page->refresh();
+        $this->assertStringMatchesFormat('%A<p%A>test<img src="http://localhost/uploads/images/gallery/%A.jpeg">%A</p>%A', $page->html);
+
+        $matches = [];
+        preg_match('/src="https:\/\/p.rizon.top:443\/http\/localhost(.*?)"/', $page->html, $matches);
+        $imagePath = $matches[1];
+        $imageFile = public_path($imagePath);
+        $this->assertEquals(base64_decode($this->base64Jpeg), file_get_contents($imageFile));
+
+        $this->deleteImage($imagePath);
+    }
+
+    public function test_base64_images_get_extracted_when_containing_whitespace()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $base64PngWithWhitespace = "iVBORw0KGg\noAAAANSUhE\tUgAAAAEAAAA BCA   YAAAAfFcSJAAA\n\t ACklEQVR4nGMAAQAABQAB";
+        $base64PngWithoutWhitespace = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQAB';
+        $this->put($page->getUrl(), [
+            'name' => $page->name, 'summary' => '',
+            'html' => '<p>test<img src="data:image/png;base64,'.$base64PngWithWhitespace.'"/></p>',
+        ]);
+
+        $page->refresh();
+        $this->assertStringMatchesFormat('%A<p%A>test<img src="http://localhost/uploads/images/gallery/%A.png">%A</p>%A', $page->html);
+
+        $matches = [];
+        preg_match('/src="https:\/\/p.rizon.top:443\/http\/localhost(.*?)"/', $page->html, $matches);
+        $imagePath = $matches[1];
+        $imageFile = public_path($imagePath);
+        $this->assertEquals(base64_decode($base64PngWithoutWhitespace), file_get_contents($imageFile));
+
+        $this->deleteImage($imagePath);
+    }
+
+    public function test_base64_images_blanked_if_not_supported_extension_for_extract()
+    {
+        $this->asEditor();
+        $page = Page::query()->first();
+
+        $this->put($page->getUrl(), [
+            'name' => $page->name, 'summary' => '',
+            'html' => '<p>test<img src="data:image/jiff;base64,'.$this->base64Jpeg.'"/></p>',
+        ]);
+
+        $page->refresh();
+        $this->assertStringContainsString('<img src=""', $page->html);
+    }
 }
index 4fc6b9c16175b992cd5784ca5c7cd5729118b1fe..a6f6f9d508e070b0c5a41b007a6ad72a72a1ee58 100644 (file)
@@ -6,6 +6,22 @@ use Tests\TestCase;
 
 class PageTest extends TestCase
 {
+
+    public function test_page_view_when_creator_is_deleted_but_owner_exists()
+    {
+        $page = Page::query()->first();
+        $user = $this->getViewer();
+        $owner = $this->getEditor();
+        $page->created_by = $user->id;
+        $page->owned_by = $owner->id;
+        $page->save();
+        $user->delete();
+
+        $resp = $this->asAdmin()->get($page->getUrl());
+        $resp->assertStatus(200);
+        $resp->assertSeeText('Owned by ' . $owner->name);
+    }
+
     public function test_page_creation_with_markdown_content()
     {
         $this->setSettings(['app-editor' => 'markdown']);
@@ -55,6 +71,33 @@ class PageTest extends TestCase
         $redirectReq->assertNotificationContains('Page Successfully Deleted');
     }
 
+    public function test_page_full_delete_removes_all_revisions()
+    {
+        /** @var Page $page */
+        $page = Page::query()->first();
+        $page->revisions()->create([
+            'html' => '<p>ducks</p>',
+            'name' => 'my page revision',
+            'type' => 'draft',
+        ]);
+        $page->revisions()->create([
+            'html' => '<p>ducks</p>',
+            'name' => 'my page revision',
+            'type' => 'revision',
+        ]);
+
+        $this->assertDatabaseHas('page_revisions', [
+            'page_id' => $page->id,
+        ]);
+
+        $this->asEditor()->delete($page->getUrl());
+        $this->asAdmin()->post('/settings/recycle-bin/empty');
+
+        $this->assertDatabaseMissing('page_revisions', [
+            'page_id' => $page->id,
+        ]);
+    }
+
     public function test_page_copy()
     {
         $page = Page::first();
@@ -145,4 +188,27 @@ class PageTest extends TestCase
             'book_id' => $newBook->id,
         ]);
     }
+
+    public function test_empty_markdown_still_saves_without_error()
+    {
+        $this->setSettings(['app-editor' => 'markdown']);
+        $book = Book::query()->first();
+
+        $this->asEditor()->get($book->getUrl('/create-page'));
+        $draft = Page::query()->where('book_id', '=', $book->id)
+            ->where('draft', '=', true)->first();
+
+        $details = [
+            'name' => 'my page',
+            'markdown' => '',
+        ];
+        $resp = $this->post($book->getUrl("/draft/{$draft->id}"), $details);
+        $resp->assertRedirect();
+
+        $this->assertDatabaseHas('pages', [
+            'markdown' => $details['markdown'],
+            'id' => $draft->id,
+            'draft' => false
+        ]);
+    }
 }
\ No newline at end of file
index 3ad10641ef3d0196ce15800d18d0ed149e3ed50a..62ebf5f1ba4e68840fddad353a0f10020a5c982b 100644 (file)
@@ -1,28 +1,23 @@
 <?php namespace Tests\Entity;
 
-use BookStack\Entities\Models\Book;
-use BookStack\Entities\Models\Chapter;
 use BookStack\Actions\Tag;
 use BookStack\Entities\Models\Entity;
 use BookStack\Entities\Models\Page;
-use BookStack\Auth\Permissions\PermissionService;
-use Tests\BrowserKitTest;
+use Tests\TestCase;
 
-class TagTest extends BrowserKitTest
+class TagTest extends TestCase
 {
 
     protected $defaultTagCount = 20;
 
     /**
      * Get an instance of a page that has many tags.
-     * @param \BookStack\Actions\Tag[]|bool $tags
-     * @return Entity
      */
-    protected function getEntityWithTags($class, $tags = false): Entity
+    protected function getEntityWithTags($class, ?array $tags = null): Entity
     {
         $entity = $class::first();
 
-        if (!$tags) {
+        if (is_null($tags)) {
             $tags = factory(Tag::class, $this->defaultTagCount)->make();
         }
 
@@ -40,12 +35,12 @@ class TagTest extends BrowserKitTest
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'county']));
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'planet']));
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'plans']));
-        $page = $this->getEntityWithTags(Page::class, $attrs);
+        $page = $this->getEntityWithTags(Page::class, $attrs->all());
 
-        $this->asAdmin()->get('/ajax/tags/suggest/names?search=dog')->seeJsonEquals([]);
-        $this->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country', 'county']);
-        $this->get('/ajax/tags/suggest/names?search=cou')->seeJsonEquals(['country', 'county']);
-        $this->get('/ajax/tags/suggest/names?search=pla')->seeJsonEquals(['planet', 'plans']);
+        $this->asAdmin()->get('/ajax/tags/suggest/names?search=dog')->assertExactJson([]);
+        $this->get('/ajax/tags/suggest/names?search=co')->assertExactJson(['color', 'country', 'county']);
+        $this->get('/ajax/tags/suggest/names?search=cou')->assertExactJson(['country', 'county']);
+        $this->get('/ajax/tags/suggest/names?search=pla')->assertExactJson(['planet', 'plans']);
     }
 
     public function test_tag_value_suggestions()
@@ -58,34 +53,48 @@ class TagTest extends BrowserKitTest
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'county', 'value' => 'dog']));
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'planet', 'value' => 'catapult']));
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'plans', 'value' => 'dodgy']));
-        $page = $this->getEntityWithTags(Page::class, $attrs);
+        $page = $this->getEntityWithTags(Page::class, $attrs->all());
 
-        $this->asAdmin()->get('/ajax/tags/suggest/values?search=ora')->seeJsonEquals([]);
-        $this->get('/ajax/tags/suggest/values?search=cat')->seeJsonEquals(['cats', 'cattery', 'catapult']);
-        $this->get('/ajax/tags/suggest/values?search=do')->seeJsonEquals(['dog', 'dodgy']);
-        $this->get('/ajax/tags/suggest/values?search=cas')->seeJsonEquals(['castle']);
+        $this->asAdmin()->get('/ajax/tags/suggest/values?search=ora')->assertExactJson([]);
+        $this->get('/ajax/tags/suggest/values?search=cat')->assertExactJson(['cats', 'cattery', 'catapult']);
+        $this->get('/ajax/tags/suggest/values?search=do')->assertExactJson(['dog', 'dodgy']);
+        $this->get('/ajax/tags/suggest/values?search=cas')->assertExactJson(['castle']);
     }
 
     public function test_entity_permissions_effect_tag_suggestions()
     {
-        $permissionService = $this->app->make(PermissionService::class);
-
         // Create some tags with similar names to test with and save to a page
         $attrs = collect();
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'country']));
         $attrs = $attrs->merge(factory(Tag::class, 5)->make(['name' => 'color']));
-        $page = $this->getEntityWithTags(Page::class, $attrs);
+        $page = $this->getEntityWithTags(Page::class, $attrs->all());
 
-        $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']);
-        $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']);
+        $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertExactJson(['color', 'country']);
+        $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertExactJson(['color', 'country']);
 
         // Set restricted permission the page
         $page->restricted = true;
         $page->save();
         $page->rebuildPermissions();
 
-        $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals(['color', 'country']);
-        $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->seeJsonEquals([]);
+        $this->asAdmin()->get('/ajax/tags/suggest/names?search=co')->assertExactJson(['color', 'country']);
+        $this->asEditor()->get('/ajax/tags/suggest/names?search=co')->assertExactJson([]);
+    }
+
+    public function test_tags_shown_on_search_listing()
+    {
+        $tags = [
+            factory(Tag::class)->make(['name' => 'category', 'value' => 'buckets']),
+            factory(Tag::class)->make(['name' => 'color', 'value' => 'red']),
+        ];
+
+        $page = $this->getEntityWithTags(Page::class, $tags);
+        $resp = $this->asEditor()->get("/search?term=[category]");
+        $resp->assertSee($page->name);
+        $resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'category');
+        $resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'buckets');
+        $resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'color');
+        $resp->assertElementContains('[href="' . $page->getUrl() . '"]', 'red');
     }
 
 }
index 1558df78d1c200d59b29d559ceef38f687b81e4f..6b69355fcd3a8cc218469557e0b45b48258e190c 100644 (file)
@@ -38,4 +38,11 @@ class ErrorTest extends TestCase
 
         $this->assertCount(1, $handler->getRecords());
     }
+
+    public function test_access_to_non_existing_image_location_provides_404_response()
+    {
+        $resp = $this->actingAs($this->getViewer())->get('/uploads/images/gallery/2021-05/anonexistingimage.png');
+        $resp->assertStatus(404);
+        $resp->assertSeeText('Image Not Found');
+    }
 }
\ No newline at end of file
diff --git a/tests/FavouriteTest.php b/tests/FavouriteTest.php
new file mode 100644 (file)
index 0000000..7209063
--- /dev/null
@@ -0,0 +1,118 @@
+<?php
+
+use BookStack\Actions\Favourite;
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use Tests\TestCase;
+
+class FavouriteTest extends TestCase
+{
+
+    public function test_page_add_favourite_flow()
+    {
+        $page = Page::query()->first();
+        $editor = $this->getEditor();
+
+        $resp = $this->actingAs($editor)->get($page->getUrl());
+        $resp->assertElementContains('button', 'Favourite');
+        $resp->assertElementExists('form[method="POST"][action$="/favourites/add"]');
+
+        $resp = $this->post('/favourites/add', [
+            'type' => get_class($page),
+            'id' => $page->id,
+        ]);
+        $resp->assertRedirect($page->getUrl());
+        $resp->assertSessionHas('success', "\"{$page->name}\" has been added to your favourites");
+
+        $this->assertDatabaseHas('favourites', [
+            'user_id' => $editor->id,
+            'favouritable_type' => $page->getMorphClass(),
+            'favouritable_id' => $page->id,
+        ]);
+    }
+
+    public function test_page_remove_favourite_flow()
+    {
+        $page = Page::query()->first();
+        $editor = $this->getEditor();
+        Favourite::query()->forceCreate([
+            'user_id' => $editor->id,
+            'favouritable_id' => $page->id,
+            'favouritable_type' => $page->getMorphClass(),
+        ]);
+
+        $resp = $this->actingAs($editor)->get($page->getUrl());
+        $resp->assertElementContains('button', 'Unfavourite');
+        $resp->assertElementExists('form[method="POST"][action$="/favourites/remove"]');
+
+        $resp = $this->post('/favourites/remove', [
+            'type' => get_class($page),
+            'id' => $page->id,
+        ]);
+        $resp->assertRedirect($page->getUrl());
+        $resp->assertSessionHas('success', "\"{$page->name}\" has been removed from your favourites");
+
+        $this->assertDatabaseMissing('favourites', [
+            'user_id' => $editor->id,
+        ]);
+    }
+
+    public function test_book_chapter_shelf_pages_contain_favourite_button()
+    {
+        $entities = [
+            Bookshelf::query()->first(),
+            Book::query()->first(),
+            Chapter::query()->first(),
+        ];
+        $this->actingAs($this->getEditor());
+
+        foreach ($entities as $entity) {
+            $resp = $this->get($entity->getUrl());
+            $resp->assertElementExists('form[method="POST"][action$="/favourites/add"]');
+        }
+    }
+
+    public function test_header_contains_link_to_favourites_page_when_logged_in()
+    {
+        $this->setSettings(['app-public' => 'true']);
+        $this->get('/')->assertElementNotContains('header', 'My Favourites');
+        $this->actingAs($this->getViewer())->get('/')->assertElementContains('header a', 'My Favourites');
+    }
+
+    public function test_favourites_shown_on_homepage()
+    {
+        $editor = $this->getEditor();
+
+        $resp = $this->actingAs($editor)->get('/');
+        $resp->assertElementNotExists('#top-favourites');
+
+        /** @var Page $page */
+        $page = Page::query()->first();
+        $page->favourites()->save((new Favourite)->forceFill(['user_id' => $editor->id]));
+
+        $resp = $this->get('/');
+        $resp->assertElementExists('#top-favourites');
+        $resp->assertElementContains('#top-favourites', $page->name);
+    }
+
+    public function test_favourites_list_page_shows_favourites_and_has_working_pagination()
+    {
+        /** @var Page $page */
+        $page = Page::query()->first();
+        $editor = $this->getEditor();
+
+        $resp = $this->actingAs($editor)->get('/favourites');
+        $resp->assertDontSee($page->name);
+
+        $page->favourites()->save((new Favourite)->forceFill(['user_id' => $editor->id]));
+
+        $resp = $this->get('/favourites');
+        $resp->assertSee($page->name);
+
+        $resp = $this->get('/favourites?page=2');
+        $resp->assertDontSee($page->name);
+    }
+
+}
\ No newline at end of file
index 943a3160a1915af2a803ed88cd82411e67f2336d..a8e33465d108b1b9ad5000ef796ce761789b3db4 100644 (file)
@@ -1,6 +1,9 @@
 <?php namespace Tests;
 
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
 use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Page;
 
 class HomepageTest extends TestCase
 {
@@ -141,4 +144,14 @@ class HomepageTest extends TestCase
         $homeVisit->assertElementContains('.content-wrap', $shelf->name);
         $homeVisit->assertElementContains('.content-wrap', $book->name);
     }
+
+    public function test_new_users_dont_have_any_recently_viewed()
+    {
+        $user = factory(User::class)->create();
+        $viewRole = Role::getRole('Viewer');
+        $user->attachRole($viewRole);
+
+        $homeVisit = $this->actingAs($user)->get('/');
+        $homeVisit->assertElementContains('#recently-viewed', 'You have not viewed any pages');
+    }
 }
diff --git a/tests/OpenGraphTest.php b/tests/OpenGraphTest.php
new file mode 100644 (file)
index 0000000..653f2e5
--- /dev/null
@@ -0,0 +1,102 @@
+<?php namespace Tests;
+
+use BookStack\Entities\Models\Book;
+use BookStack\Entities\Models\Bookshelf;
+use BookStack\Entities\Models\Chapter;
+use BookStack\Entities\Models\Page;
+use BookStack\Entities\Repos\BookRepo;
+use BookStack\Entities\Repos\BookshelfRepo;
+use Illuminate\Support\Str;
+use Tests\Uploads\UsesImages;
+
+class OpenGraphTest extends TestCase
+{
+    use UsesImages;
+
+    public function test_page_tags()
+    {
+        $page = Page::query()->first();
+        $resp = $this->asEditor()->get($page->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($page->getShortName() . ' | BookStack', $tags['title']);
+        $this->assertEquals($page->getUrl(), $tags['url']);
+        $this->assertEquals(Str::limit($page->text, 100, '...'), $tags['description']);
+    }
+
+    public function test_chapter_tags()
+    {
+        $chapter = Chapter::query()->first();
+        $resp = $this->asEditor()->get($chapter->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($chapter->getShortName() . ' | BookStack', $tags['title']);
+        $this->assertEquals($chapter->getUrl(), $tags['url']);
+        $this->assertEquals(Str::limit($chapter->description, 100, '...'), $tags['description']);
+    }
+
+    public function test_book_tags()
+    {
+        $book = Book::query()->first();
+        $resp = $this->asEditor()->get($book->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($book->getShortName() . ' | BookStack', $tags['title']);
+        $this->assertEquals($book->getUrl(), $tags['url']);
+        $this->assertEquals(Str::limit($book->description, 100, '...'), $tags['description']);
+        $this->assertArrayNotHasKey('image', $tags);
+
+        // Test image set if image has cover image
+        $bookRepo = app(BookRepo::class);
+        $bookRepo->updateCoverImage($book, $this->getTestImage('image.png'));
+        $resp = $this->asEditor()->get($book->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($book->getBookCover(), $tags['image']);
+    }
+
+    public function test_shelf_tags()
+    {
+        $shelf = Bookshelf::query()->first();
+        $resp = $this->asEditor()->get($shelf->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($shelf->getShortName() . ' | BookStack', $tags['title']);
+        $this->assertEquals($shelf->getUrl(), $tags['url']);
+        $this->assertEquals(Str::limit($shelf->description, 100, '...'), $tags['description']);
+        $this->assertArrayNotHasKey('image', $tags);
+
+        // Test image set if image has cover image
+        $shelfRepo = app(BookshelfRepo::class);
+        $shelfRepo->updateCoverImage($shelf, $this->getTestImage('image.png'));
+        $resp = $this->asEditor()->get($shelf->getUrl());
+        $tags = $this->getOpenGraphTags($resp);
+
+        $this->assertEquals($shelf->getBookCover(), $tags['image']);
+    }
+
+    /**
+     * Parse the open graph tags from a test response.
+     */
+    protected function getOpenGraphTags(TestResponse $resp): array
+    {
+        $tags = [];
+
+        libxml_use_internal_errors(true);
+        $doc = new \DOMDocument();
+        $doc->loadHTML($resp->getContent());
+        $metaElems = $doc->getElementsByTagName('meta');
+        /** @var \DOMElement $elem */
+        foreach ($metaElems as $elem) {
+            $prop = $elem->getAttribute('property');
+            $name = explode(':', $prop)[1] ?? null;
+            if ($name) {
+                $tags[$name] = $elem->getAttribute('content');
+            }
+        }
+
+        return $tags;
+    }
+
+
+}
\ No newline at end of file
index 7caefd0ac45a5a1b9b9a5b21a3f89605fe4e3754..7dbf467bd838d8d82ece92e72b6b298ec0b88146 100644 (file)
@@ -8,6 +8,7 @@ use BookStack\Auth\User;
 use BookStack\Entities\Models\Book;
 use BookStack\Entities\Models\Chapter;
 use BookStack\Entities\Models\Page;
+use Illuminate\Support\Facades\View;
 
 class PublicActionTest extends TestCase
 {
@@ -115,10 +116,12 @@ class PublicActionTest extends TestCase
     public function test_content_not_listed_on_404_for_public_users()
     {
         $page = Page::query()->first();
+        $page->fill(['name' => 'my testing random unique page name'])->save();
         $this->asAdmin()->get($page->getUrl()); // Fake visit to show on recents
         $resp = $this->get('/cats/dogs/hippos');
         $resp->assertStatus(404);
         $resp->assertSee($page->name);
+        View::share('pageTitle', '');
 
         Auth::logout();
         $resp = $this->get('/cats/dogs/hippos');
index 78c1f3b1825121a9c2a8ece027e29fe2fd087f3d..a98f01e94e553f2f7bec643e1c736cd79e749b03 100644 (file)
@@ -82,6 +82,14 @@ trait SharedTestHelpers
         return $user;
     }
 
+    /**
+     * Get a user that's not a system user such as the guest user.
+     */
+    public function getNormalUser()
+    {
+        return User::query()->where('system_name', '=', null)->get()->last();
+    }
+
     /**
      * Regenerate the permission for an entity.
      */
index 7a0cd49cb54696353841012b605b3404565298fb..be3fc4ebdd1d4c5bdfb8eb3c78ce2afb297832be 100644 (file)
@@ -1,6 +1,5 @@
 <?php namespace Tests;
 
-use BookStack\Auth\Access\SocialAuthService;
 use BookStack\Auth\User;
 use BookStack\Entities\Models\Page;
 use BookStack\Entities\Tools\PageContent;
@@ -149,7 +148,7 @@ class ThemeTest extends TestCase
         $this->setSettings(['registration-enabled' => 'true']);
 
         $user = factory(User::class)->make();
-        $this->post('/register', ['email' => $user->email, 'name' => $user->name,  'password' => 'password']);
+        $this->post('/register', ['email' => $user->email, 'name' => $user->name, 'password' => 'password']);
 
         $this->assertCount(2, $args);
         $this->assertEquals('standard', $args[0]);
@@ -184,6 +183,28 @@ class ThemeTest extends TestCase
         $loginResp->assertSee('Super Cat Name');
     }
 
+
+    public function test_add_social_driver_allows_a_configure_for_redirect_callback_to_be_passed()
+    {
+        Theme::addSocialDriver(
+            'discord',
+            [
+                'client_id' => 'abc123',
+                'client_secret' => 'def456',
+                'name' => 'Super Cat Name',
+            ],
+            'SocialiteProviders\Discord\DiscordExtendSocialite@handle',
+            function ($driver) {
+                $driver->with(['donkey' => 'donut']);
+            }
+        );
+
+        $loginResp = $this->get('/login/service/discord');
+        $redirect = $loginResp->headers->get('location');
+        $this->assertStringContainsString('donkey=donut', $redirect);
+    }
+
+
     protected function usingThemeFolder(callable $callback)
     {
         // Create a folder and configure a theme
index 1d4decc2b330036feed751d396e52215770128ef..0833ffbd8858c4a049ecd7354a58800d37d8b947 100644 (file)
@@ -67,12 +67,23 @@ class ConfigTest extends TestCase
         $this->checkEnvConfigResult('APP_URL', '', 'session.path', '/');
     }
 
+    public function test_saml2_idp_authn_context_string_parsed_as_space_separated_array()
+    {
+        $this->checkEnvConfigResult(
+            'SAML2_IDP_AUTHNCONTEXT',
+            'urn:federation:authentication:windows urn:federation:authentication:linux',
+            'saml2.onelogin.security.requestedAuthnContext',
+            ['urn:federation:authentication:windows', 'urn:federation:authentication:linux']
+        );
+    }
+
     /**
      * Set an environment variable of the given name and value
      * then check the given config key to see if it matches the given result.
      * Providing a null $envVal clears the variable.
+     * @param mixed $expectedResult
      */
-    protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, string $expectedResult)
+    protected function checkEnvConfigResult(string $envName, ?string $envVal, string $configKey, $expectedResult)
     {
         $this->runWithEnv($envName, $envVal, function() use ($configKey, $expectedResult) {
             $this->assertEquals($expectedResult, config($configKey));
index 1ca9ea23b17d5d04101c2203173d394b3455b379..55a5aa84fed023e82f2c384d8b13327231dae8c2 100644 (file)
@@ -4,11 +4,9 @@ use BookStack\Entities\Tools\TrashCan;
 use BookStack\Entities\Repos\PageRepo;
 use BookStack\Uploads\Attachment;
 use BookStack\Entities\Models\Page;
-use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Uploads\AttachmentService;
 use Illuminate\Http\UploadedFile;
 use Tests\TestCase;
-use Tests\TestResponse;
 
 class AttachmentTest extends TestCase
 {
@@ -57,7 +55,7 @@ class AttachmentTest extends TestCase
 
     public function test_file_upload()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $admin = $this->getAdmin();
         $fileName = 'upload_test_file.txt';
@@ -85,7 +83,7 @@ class AttachmentTest extends TestCase
 
     public function test_file_upload_does_not_use_filename()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $fileName = 'upload_test_file.txt';
 
 
@@ -99,7 +97,7 @@ class AttachmentTest extends TestCase
 
     public function test_file_display_and_access()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $fileName = 'upload_test_file.txt';
 
@@ -119,7 +117,7 @@ class AttachmentTest extends TestCase
 
     public function test_attaching_link_to_page()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->asAdmin();
 
@@ -156,7 +154,7 @@ class AttachmentTest extends TestCase
 
     public function test_attachment_updating()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
 
         $attachment = $this->createAttachment($page);
@@ -180,7 +178,7 @@ class AttachmentTest extends TestCase
 
     public function test_file_deletion()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $fileName = 'deletion_test.txt';
         $this->uploadFile($fileName, $page->id);
@@ -202,7 +200,7 @@ class AttachmentTest extends TestCase
 
     public function test_attachment_deletion_on_page_deletion()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $fileName = 'deletion_test.txt';
         $this->uploadFile($fileName, $page->id);
@@ -230,7 +228,7 @@ class AttachmentTest extends TestCase
     {
         $admin = $this->getAdmin();
         $viewer = $this->getViewer();
-        $page = Page::first(); /** @var Page $page */
+        $page = Page::query()->first(); /** @var Page $page */
 
         $this->actingAs($admin);
         $fileName = 'permission_test.txt';
@@ -253,7 +251,7 @@ class AttachmentTest extends TestCase
 
     public function test_data_and_js_links_cannot_be_attached_to_a_page()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
 
         $badLinks = [
@@ -291,4 +289,22 @@ class AttachmentTest extends TestCase
             ]);
         }
     }
+
+    public function test_file_access_with_open_query_param_provides_inline_response_with_correct_content_type()
+    {
+        $page = Page::query()->first();
+        $this->asAdmin();
+        $fileName = 'upload_test_file.txt';
+
+        $upload = $this->uploadFile($fileName, $page->id);
+        $upload->assertStatus(200);
+        $attachment = Attachment::query()->orderBy('id', 'desc')->take(1)->first();
+
+        $attachmentGet = $this->get($attachment->getUrl(true));
+        // http-foundation/Response does some 'fixing' of responses to add charsets to text responses.
+        $attachmentGet->assertHeader('Content-Type', 'text/plain; charset=UTF-8');
+        $attachmentGet->assertHeader('Content-Disposition', "inline; filename=\"upload_test_file.txt\"");
+
+        $this->deleteUploads();
+    }
 }
index c03d15dd751a3d044ecdff00c0bdaa7b3c2c7012..95332565e5761673cdafa5489aecced690a7faaf 100644 (file)
@@ -14,7 +14,7 @@ class ImageTest extends TestCase
 
     public function test_image_upload()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->actingAs($admin);
 
@@ -38,7 +38,7 @@ class ImageTest extends TestCase
 
     public function test_image_display_thumbnail_generation_does_not_increase_image_size()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->actingAs($admin);
 
@@ -108,7 +108,7 @@ class ImageTest extends TestCase
 
     public function test_image_usage()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $editor = $this->getEditor();
         $this->actingAs($editor);
 
@@ -128,7 +128,7 @@ class ImageTest extends TestCase
 
     public function test_php_files_cannot_be_uploaded()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->actingAs($admin);
 
@@ -150,7 +150,7 @@ class ImageTest extends TestCase
 
     public function test_php_like_files_cannot_be_uploaded()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->actingAs($admin);
 
@@ -202,7 +202,7 @@ class ImageTest extends TestCase
         ];
         foreach ($badNames as $name) {
             $galleryFile = $this->getTestImage($name);
-            $page = Page::first();
+            $page = Page::query()->first();
             $badPath = $this->getTestImagePath('gallery', $name);
             $this->deleteImage($badPath);
 
@@ -227,7 +227,7 @@ class ImageTest extends TestCase
         config()->set('filesystems.images', 'local_secure');
         $this->asEditor();
         $galleryFile = $this->getTestImage('my-secure-test-upload.png');
-        $page = Page::first();
+        $page = Page::query()->first();
         $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 
         $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
@@ -245,7 +245,7 @@ class ImageTest extends TestCase
         config()->set('filesystems.images', 'local_secure');
         $this->asEditor();
         $galleryFile = $this->getTestImage('my-secure-test-upload.png');
-        $page = Page::first();
+        $page = Page::query()->first();
         $expectedPath = storage_path('uploads/images/gallery/' . Date('Y-m') . '/my-secure-test-upload.png');
 
         $upload = $this->call('POST', '/images/gallery', ['uploaded_to' => $page->id], [], ['file' => $galleryFile], []);
@@ -282,7 +282,7 @@ class ImageTest extends TestCase
 
     public function test_image_delete()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $imageName = 'first-image.png';
         $relPath = $this->getTestImagePath('gallery', $imageName);
@@ -304,7 +304,7 @@ class ImageTest extends TestCase
 
     public function test_image_delete_does_not_delete_similar_images()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $this->asAdmin();
         $imageName = 'first-image.png';
 
@@ -383,7 +383,7 @@ class ImageTest extends TestCase
 
     public function test_deleted_unused_images()
     {
-        $page = Page::first();
+        $page = Page::query()->first();
         $admin = $this->getAdmin();
         $this->actingAs($admin);
 
diff --git a/version b/version
index 92d9faea7781a62c65cccb97e9dd1292d1184271..0d86fac788e718d59afcf33dbc4b497fa1cbe152 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-v0.32-dev
+v21.06-dev