]> BookStack Code Mirror - bookstack/commitdiff
Merge pull request #1762 from dellamina/updateItalianTranslation
authorDan Brown <redacted>
Fri, 27 Dec 2019 16:18:04 +0000 (16:18 +0000)
committerGitHub <redacted>
Fri, 27 Dec 2019 16:18:04 +0000 (16:18 +0000)
Update Italian translation

83 files changed:
.env.example.complete
.github/ISSUE_TEMPLATE/bug_report.md
.github/ISSUE_TEMPLATE/feature_request.md
.github/ISSUE_TEMPLATE/language_request.md [new file with mode: 0644]
.github/translators.txt [new file with mode: 0644]
.gitignore
app/Auth/Access/ExternalAuthService.php [new file with mode: 0644]
app/Auth/Access/LdapService.php
app/Auth/Access/Saml2Service.php [new file with mode: 0644]
app/Auth/Access/SocialAuthService.php
app/Auth/Role.php
app/Config/saml2.php [new file with mode: 0644]
app/Config/services.php
app/Config/setting-defaults.php
app/Console/Commands/CopyShelfPermissions.php [new file with mode: 0644]
app/Entities/Repos/BookshelfRepo.php
app/Exceptions/JsonDebugException.php [new file with mode: 0644]
app/Exceptions/SamlException.php [new file with mode: 0644]
app/Http/Controllers/Auth/LoginController.php
app/Http/Controllers/Auth/RegisterController.php
app/Http/Controllers/Auth/Saml2Controller.php [new file with mode: 0644]
app/Http/Controllers/PageController.php
app/Http/Middleware/VerifyCsrfToken.php
app/Settings/SettingService.php
app/Uploads/ImageRepo.php
app/Uploads/ImageService.php
composer.json
composer.lock
package-lock.json
package.json
phpunit.xml
readme.md
resources/icons/fullscreen.svg [new file with mode: 0644]
resources/icons/saml2.svg [new file with mode: 0644]
resources/js/components/collapsible.js
resources/js/components/index.js
resources/js/components/markdown-editor.js
resources/js/components/overlay.js
resources/js/components/setting-app-color-picker.js
resources/js/components/setting-color-picker.js [new file with mode: 0644]
resources/js/components/wysiwyg-editor.js
resources/js/services/animations.js
resources/js/services/clipboard.js [new file with mode: 0644]
resources/js/services/code.js
resources/js/vues/code-editor.js
resources/lang/de/errors.php
resources/lang/de_informal/errors.php
resources/lang/en/common.php
resources/lang/en/errors.php
resources/lang/en/settings.php
resources/lang/ko/activities.php
resources/lang/ko/auth.php
resources/lang/ko/common.php
resources/lang/ko/components.php
resources/lang/ko/entities.php
resources/lang/ko/errors.php
resources/lang/ko/passwords.php
resources/lang/ko/settings.php
resources/lang/ko/validation.php
resources/sass/_codemirror.scss
resources/sass/_colors.scss
resources/sass/_forms.scss
resources/sass/_html.scss
resources/sass/_pages.scss
resources/views/auth/login.blade.php
resources/views/auth/register.blade.php
resources/views/common/header.blade.php
resources/views/components/setting-entity-color-picker.blade.php [new file with mode: 0644]
resources/views/pages/markdown-editor.blade.php
resources/views/pages/show.blade.php
resources/views/partials/custom-styles.blade.php
resources/views/settings/index.blade.php
resources/views/settings/roles/form.blade.php
resources/views/users/form.blade.php
routes/web.php
tests/Auth/LdapTest.php
tests/Auth/Saml2Test.php [new file with mode: 0644]
tests/CommandsTest.php
tests/Entity/EntityTest.php
tests/Permissions/RolesTest.php
tests/Uploads/ImageTest.php
tests/Uploads/UsesImages.php
tests/test-data/compressed.png [new file with mode: 0644]

index c4c3f0b85ff1192839395d1cc7edb3b965802b8f..a13c8b7d086ee2f8b37ee27a1bf5ed97bd486b3c 100644 (file)
@@ -191,6 +191,7 @@ LDAP_PASS=false
 LDAP_USER_FILTER=false
 LDAP_VERSION=false
 LDAP_TLS_INSECURE=false
+LDAP_ID_ATTRIBUTE=uid
 LDAP_EMAIL_ATTRIBUTE=mail
 LDAP_DISPLAY_NAME_ATTRIBUTE=cn
 LDAP_FOLLOW_REFERRALS=true
@@ -201,6 +202,28 @@ LDAP_USER_TO_GROUPS=false
 LDAP_GROUP_ATTRIBUTE="memberOf"
 LDAP_REMOVE_FROM_GROUPS=false
 
+# SAML authentication configuration
+# Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/
+SAML2_NAME=SSO
+SAML2_ENABLED=false
+SAML2_AUTO_REGISTER=true
+SAML2_EMAIL_ATTRIBUTE=email
+SAML2_DISPLAY_NAME_ATTRIBUTES=username
+SAML2_EXTERNAL_ID_ATTRIBUTE=null
+SAML2_IDP_ENTITYID=null
+SAML2_IDP_SSO=null
+SAML2_IDP_SLO=null
+SAML2_IDP_x509=null
+SAML2_ONELOGIN_OVERRIDES=null
+SAML2_DUMP_USER_DETAILS=false
+SAML2_AUTOLOAD_METADATA=false
+
+# SAML group sync configuration
+# Refer to https://www.bookstackapp.com/docs/admin/saml2-auth/
+SAML2_USER_TO_GROUPS=false
+SAML2_GROUP_ATTRIBUTE=group
+SAML2_REMOVE_FROM_GROUPS=false
+
 # Disable default third-party services such as Gravatar and Draw.IO
 # Service-specific options will override this option
 DISABLE_EXTERNAL_SERVICES=false
index 8b3d29c2d291d444ecf235e9744056fd8866a2c9..c4444f242344d19294f14ba4d3edd1cf3a604600 100644 (file)
@@ -1,5 +1,5 @@
 ---
-name: Bug report
+name: Bug Report
 about: Create a report to help us improve
 
 ---
index 7f38b9cdc2a19f93b2aaf2ab013ff8656b4eb4b8..781cca5b8de289d57e739fe981af6f80674be348 100644 (file)
@@ -1,5 +1,5 @@
 ---
-name: Feature request
+name: Feature Request
 about: Suggest an idea for this project
 
 ---
diff --git a/.github/ISSUE_TEMPLATE/language_request.md b/.github/ISSUE_TEMPLATE/language_request.md
new file mode 100644 (file)
index 0000000..249ef78
--- /dev/null
@@ -0,0 +1,13 @@
+---
+name: Language Request
+about: Request a new language to be added to Crowdin for you to translate
+
+---
+
+### Language To Add
+
+_Specify here the language you want to add._
+
+----
+
+_This issue template is to request a new language be added to our [Crowdin translation management project](https://crowdin.com/project/bookstack). Please don't use this template to request a new language that you are not prepared to provide translations for._   
\ No newline at end of file
diff --git a/.github/translators.txt b/.github/translators.txt
new file mode 100644 (file)
index 0000000..5435cfc
--- /dev/null
@@ -0,0 +1,52 @@
+Name :: Languages
+@robertlandes :: German
+@SergioMendolia :: French
+@NakaharaL :: Portuguese, Brazilian
+@ReeseSebastian :: German
+@arietimmerman :: Dutch
+@diegoseso :: Spanish
+@S64 :: Japanese
+@JachuPL :: Polish
+@Joorem :: French
+@timoschwarzer :: German
+@sanderdw :: Dutch
+@lbguilherme :: Portuguese, Brazilian
+@marcusforsberg :: Swedish
+@artur-trzesiok :: Polish
+@Alwaysin :: French
+@msaus :: Japanese
+@moucho :: Spanish
+@vriic :: German
+@DeehSlash :: Portuguese, Brazilian
+@alex2702 :: German
+@nicobubulle :: French
+@kmoj86 :: Arabic
+@houbaron :: Chinese Traditional; Chinese Simplified
+@mullinsmikey :: Russian
+@limkukhyun :: Korean
+@CliffyPrime :: German
+@kejjang :: Chinese Traditional
+@TheLastOperator :: French
+@qianmengnet :: Chinese Simplified
+@ezzra :: German Informal
+@vasiliev123 :: Polish
+@Mant1kor :: Ukrainian
+@Xiphoseer German; German Informal
+@maantje :: Dutch
+@cima :: Czech
+@agvol :: Russian
+@Hambern :: Swedish
+@NootoNooto :: Dutch
+@kostefun :: Russian
+@lucaguindani :: French
+@miles75 :: Hungarian
+@danielroehrig-mm :: German
+@oykenfurkan :: Turkish
+@qligier :: French
+cipi1965 :: Italian
+Mykola Ronik (Mantikor) :: Ukrainian
+furkanoyk :: Turkish
+m0uch0 :: Spanish
+Maxim Zalata (zlatin) :: Russian; Ukrainian
+nutsflag :: French
+Leonardo Mario Martinez (leonardo.m.martinez) :: Spanish, Argentina
\ No newline at end of file
index e5579e4a62f21ca24e6f804fdee275b9655519b6..fc0f10a000593b26f6e41b17485ced22c0f7fbe3 100644 (file)
@@ -22,4 +22,5 @@ nbproject
 .project
 .settings/
 webpack-stats.json
-.phpunit.result.cache
\ No newline at end of file
+.phpunit.result.cache
+.DS_Store
\ No newline at end of file
diff --git a/app/Auth/Access/ExternalAuthService.php b/app/Auth/Access/ExternalAuthService.php
new file mode 100644 (file)
index 0000000..4bd8f86
--- /dev/null
@@ -0,0 +1,83 @@
+<?php namespace BookStack\Auth\Access;
+
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+use Illuminate\Database\Eloquent\Builder;
+
+class ExternalAuthService
+{
+    /**
+     * Check a role against an array of group names to see if it matches.
+     * Checked against role 'external_auth_id' if set otherwise the name of the role.
+     */
+    protected function roleMatchesGroupNames(Role $role, array $groupNames): bool
+    {
+        if ($role->external_auth_id) {
+            return $this->externalIdMatchesGroupNames($role->external_auth_id, $groupNames);
+        }
+
+        $roleName = str_replace(' ', '-', trim(strtolower($role->display_name)));
+        return in_array($roleName, $groupNames);
+    }
+
+    /**
+     * Check if the given external auth ID string matches one of the given group names.
+     */
+    protected function externalIdMatchesGroupNames(string $externalId, array $groupNames): bool
+    {
+        $externalAuthIds = explode(',', strtolower($externalId));
+
+        foreach ($externalAuthIds as $externalAuthId) {
+            if (in_array(trim($externalAuthId), $groupNames)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Match an array of group names to BookStack system roles.
+     * Formats group names to be lower-case and hyphenated.
+     * @param array $groupNames
+     * @return \Illuminate\Support\Collection
+     */
+    protected function matchGroupsToSystemsRoles(array $groupNames)
+    {
+        foreach ($groupNames as $i => $groupName) {
+            $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
+        }
+
+        $roles = Role::query()->where(function (Builder $query) use ($groupNames) {
+            $query->whereIn('name', $groupNames);
+            foreach ($groupNames as $groupName) {
+                $query->orWhere('external_auth_id', 'LIKE', '%' . $groupName . '%');
+            }
+        })->get();
+
+        $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
+            return $this->roleMatchesGroupNames($role, $groupNames);
+        });
+
+        return $matchedRoles->pluck('id');
+    }
+
+    /**
+     * Sync the groups to the user roles for the current user
+     * @param \BookStack\Auth\User $user
+     * @param array $userGroups
+     */
+    public function syncWithGroups(User $user, array $userGroups)
+    {
+        // Get the ids for the roles from the names
+        $groupsAsRoles = $this->matchGroupsToSystemsRoles($userGroups);
+
+        // Sync groups
+        if ($this->config['remove_from_groups']) {
+            $user->roles()->sync($groupsAsRoles);
+            $this->userRepo->attachDefaultRole($user);
+        } else {
+            $user->roles()->syncWithoutDetaching($groupsAsRoles);
+        }
+    }
+}
index c7415e1f738e723bd18e98ea9a3f5c6a217bb697..554bc4b4811bb4a01d1985bd66577c29a340ef65 100644 (file)
@@ -1,19 +1,16 @@
 <?php namespace BookStack\Auth\Access;
 
-use BookStack\Auth\Access;
-use BookStack\Auth\Role;
 use BookStack\Auth\User;
 use BookStack\Auth\UserRepo;
 use BookStack\Exceptions\LdapException;
+use ErrorException;
 use Illuminate\Contracts\Auth\Authenticatable;
-use Illuminate\Database\Eloquent\Builder;
 
 /**
  * Class LdapService
  * Handles any app-specific LDAP tasks.
- * @package BookStack\Services
  */
-class LdapService
+class LdapService extends ExternalAuthService
 {
 
     protected $ldap;
@@ -24,10 +21,8 @@ class LdapService
 
     /**
      * LdapService constructor.
-     * @param Ldap $ldap
-     * @param \BookStack\Auth\UserRepo $userRepo
      */
-    public function __construct(Access\Ldap $ldap, UserRepo $userRepo)
+    public function __construct(Ldap $ldap, UserRepo $userRepo)
     {
         $this->ldap = $ldap;
         $this->config = config('services.ldap');
@@ -45,13 +40,10 @@ class LdapService
     }
 
     /**
-     * Search for attributes for a specific user on the ldap
-     * @param string $userName
-     * @param array $attributes
-     * @return null|array
+     * Search for attributes for a specific user on the ldap.
      * @throws LdapException
      */
-    private function getUserWithAttributes($userName, $attributes)
+    private function getUserWithAttributes(string $userName, array $attributes): ?array
     {
         $ldapConnection = $this->getConnection();
         $this->bindSystemUser($ldapConnection);
@@ -73,16 +65,15 @@ class LdapService
     /**
      * Get the details of a user from LDAP using the given username.
      * User found via configurable user filter.
-     * @param $userName
-     * @return array|null
      * @throws LdapException
      */
-    public function getUserDetails($userName)
+    public function getUserDetails(string $userName): ?array
     {
+        $idAttr = $this->config['id_attribute'];
         $emailAttr = $this->config['email_attribute'];
         $displayNameAttr = $this->config['display_name_attribute'];
 
-        $user = $this->getUserWithAttributes($userName, ['cn', 'uid', 'dn', $emailAttr, $displayNameAttr]);
+        $user = $this->getUserWithAttributes($userName, ['cn', 'dn', $idAttr, $emailAttr, $displayNameAttr]);
 
         if ($user === null) {
             return null;
@@ -90,7 +81,7 @@ class LdapService
 
         $userCn = $this->getUserResponseProperty($user, 'cn', null);
         return [
-            'uid'   => $this->getUserResponseProperty($user, 'uid', $user['dn']),
+            'uid'   => $this->getUserResponseProperty($user, $idAttr, $user['dn']),
             'name'  => $this->getUserResponseProperty($user, $displayNameAttr, $userCn),
             'dn'    => $user['dn'],
             'email' => $this->getUserResponseProperty($user, $emailAttr, null),
@@ -100,13 +91,10 @@ class LdapService
     /**
      * Get a property from an LDAP user response fetch.
      * Handles properties potentially being part of an array.
-     * @param array $userDetails
-     * @param string $propertyKey
-     * @param $defaultValue
-     * @return mixed
      */
     protected function getUserResponseProperty(array $userDetails, string $propertyKey, $defaultValue)
     {
+        $propertyKey = strtolower($propertyKey);
         if (isset($userDetails[$propertyKey])) {
             return (is_array($userDetails[$propertyKey]) ? $userDetails[$propertyKey][0] : $userDetails[$propertyKey]);
         }
@@ -115,13 +103,10 @@ class LdapService
     }
 
     /**
-     * @param Authenticatable $user
-     * @param string $username
-     * @param string $password
-     * @return bool
+     * Check if the given credentials are valid for the given user.
      * @throws LdapException
      */
-    public function validateUserCredentials(Authenticatable $user, $username, $password)
+    public function validateUserCredentials(Authenticatable $user, string $username, string $password): bool
     {
         $ldapUser = $this->getUserDetails($username);
         if ($ldapUser === null) {
@@ -135,7 +120,7 @@ class LdapService
         $ldapConnection = $this->getConnection();
         try {
             $ldapBind = $this->ldap->bind($ldapConnection, $ldapUser['dn'], $password);
-        } catch (\ErrorException $e) {
+        } catch (ErrorException $e) {
             $ldapBind = false;
         }
 
@@ -205,12 +190,10 @@ class LdapService
     }
 
     /**
-     * Parse a LDAP server string and return the host and port for
-     * a connection. Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'
-     * @param $serverString
-     * @return array
+     * Parse a LDAP server string and return the host and port for a connection.
+     * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'.
      */
-    protected function parseServerString($serverString)
+    protected function parseServerString(string $serverString): array
     {
         $serverNameParts = explode(':', $serverString);
 
@@ -227,11 +210,8 @@ class LdapService
 
     /**
      * Build a filter string by injecting common variables.
-     * @param string $filterString
-     * @param array $attrs
-     * @return string
      */
-    protected function buildFilter($filterString, array $attrs)
+    protected function buildFilter(string $filterString, array $attrs): string
     {
         $newAttrs = [];
         foreach ($attrs as $key => $attrText) {
@@ -242,12 +222,10 @@ class LdapService
     }
 
     /**
-     * Get the groups a user is a part of on ldap
-     * @param string $userName
-     * @return array
+     * Get the groups a user is a part of on ldap.
      * @throws LdapException
      */
-    public function getUserGroups($userName)
+    public function getUserGroups(string $userName): array
     {
         $groupsAttr = $this->config['group_attribute'];
         $user = $this->getUserWithAttributes($userName, [$groupsAttr]);
@@ -262,40 +240,36 @@ class LdapService
     }
 
     /**
-     * Get the parent groups of an array of groups
-     * @param array $groupsArray
-     * @param array $checked
-     * @return array
+     * Get the parent groups of an array of groups.
      * @throws LdapException
      */
-    private function getGroupsRecursive($groupsArray, $checked)
+    private function getGroupsRecursive(array $groupsArray, array $checked): array
     {
-        $groups_to_add = [];
+        $groupsToAdd = [];
         foreach ($groupsArray as $groupName) {
             if (in_array($groupName, $checked)) {
                 continue;
             }
 
-            $groupsToAdd = $this->getGroupGroups($groupName);
-            $groups_to_add = array_merge($groups_to_add, $groupsToAdd);
+            $parentGroups = $this->getGroupGroups($groupName);
+            $groupsToAdd = array_merge($groupsToAdd, $parentGroups);
             $checked[] = $groupName;
         }
-        $groupsArray = array_unique(array_merge($groupsArray, $groups_to_add), SORT_REGULAR);
 
-        if (!empty($groups_to_add)) {
-            return $this->getGroupsRecursive($groupsArray, $checked);
-        } else {
+        $groupsArray = array_unique(array_merge($groupsArray, $groupsToAdd), SORT_REGULAR);
+
+        if (empty($groupsToAdd)) {
             return $groupsArray;
         }
+
+        return $this->getGroupsRecursive($groupsArray, $checked);
     }
 
     /**
-     * Get the parent groups of a single group
-     * @param string $groupName
-     * @return array
+     * Get the parent groups of a single group.
      * @throws LdapException
      */
-    private function getGroupGroups($groupName)
+    private function getGroupGroups(string $groupName): array
     {
         $ldapConnection = $this->getConnection();
         $this->bindSystemUser($ldapConnection);
@@ -312,17 +286,14 @@ class LdapService
             return [];
         }
 
-        $groupGroups = $this->groupFilter($groups[0]);
-        return $groupGroups;
+        return $this->groupFilter($groups[0]);
     }
 
     /**
-     * Filter out LDAP CN and DN language in a ldap search return
-     * Gets the base CN (common name) of the string
-     * @param array $userGroupSearchResponse
-     * @return array
+     * Filter out LDAP CN and DN language in a ldap search return.
+     * Gets the base CN (common name) of the string.
      */
-    protected function groupFilter(array $userGroupSearchResponse)
+    protected function groupFilter(array $userGroupSearchResponse): array
     {
         $groupsAttr = strtolower($this->config['group_attribute']);
         $ldapGroups = [];
@@ -343,73 +314,12 @@ class LdapService
     }
 
     /**
-     * Sync the LDAP groups to the user roles for the current user
-     * @param \BookStack\Auth\User $user
-     * @param string $username
+     * Sync the LDAP groups to the user roles for the current user.
      * @throws LdapException
      */
     public function syncGroups(User $user, string $username)
     {
         $userLdapGroups = $this->getUserGroups($username);
-
-        // Get the ids for the roles from the names
-        $ldapGroupsAsRoles = $this->matchLdapGroupsToSystemsRoles($userLdapGroups);
-
-        // Sync groups
-        if ($this->config['remove_from_groups']) {
-            $user->roles()->sync($ldapGroupsAsRoles);
-            $this->userRepo->attachDefaultRole($user);
-        } else {
-            $user->roles()->syncWithoutDetaching($ldapGroupsAsRoles);
-        }
-    }
-
-    /**
-     * Match an array of group names from LDAP to BookStack system roles.
-     * Formats LDAP group names to be lower-case and hyphenated.
-     * @param array $groupNames
-     * @return \Illuminate\Support\Collection
-     */
-    protected function matchLdapGroupsToSystemsRoles(array $groupNames)
-    {
-        foreach ($groupNames as $i => $groupName) {
-            $groupNames[$i] = str_replace(' ', '-', trim(strtolower($groupName)));
-        }
-
-        $roles = Role::query()->where(function (Builder $query) use ($groupNames) {
-            $query->whereIn('name', $groupNames);
-            foreach ($groupNames as $groupName) {
-                $query->orWhere('external_auth_id', 'LIKE', '%' . $groupName . '%');
-            }
-        })->get();
-
-        $matchedRoles = $roles->filter(function (Role $role) use ($groupNames) {
-            return $this->roleMatchesGroupNames($role, $groupNames);
-        });
-
-        return $matchedRoles->pluck('id');
-    }
-
-    /**
-     * Check a role against an array of group names to see if it matches.
-     * Checked against role 'external_auth_id' if set otherwise the name of the role.
-     * @param \BookStack\Auth\Role $role
-     * @param array $groupNames
-     * @return bool
-     */
-    protected function roleMatchesGroupNames(Role $role, array $groupNames)
-    {
-        if ($role->external_auth_id) {
-            $externalAuthIds = explode(',', strtolower($role->external_auth_id));
-            foreach ($externalAuthIds as $externalAuthId) {
-                if (in_array(trim($externalAuthId), $groupNames)) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        $roleName = str_replace(' ', '-', trim(strtolower($role->display_name)));
-        return in_array($roleName, $groupNames);
+        $this->syncWithGroups($user, $userLdapGroups);
     }
 }
diff --git a/app/Auth/Access/Saml2Service.php b/app/Auth/Access/Saml2Service.php
new file mode 100644 (file)
index 0000000..c1038e7
--- /dev/null
@@ -0,0 +1,395 @@
+<?php namespace BookStack\Auth\Access;
+
+use BookStack\Auth\User;
+use BookStack\Auth\UserRepo;
+use BookStack\Exceptions\JsonDebugException;
+use BookStack\Exceptions\SamlException;
+use Exception;
+use Illuminate\Support\Str;
+use OneLogin\Saml2\Auth;
+use OneLogin\Saml2\Error;
+use OneLogin\Saml2\IdPMetadataParser;
+use OneLogin\Saml2\ValidationError;
+
+/**
+ * Class Saml2Service
+ * Handles any app-specific SAML tasks.
+ */
+class Saml2Service extends ExternalAuthService
+{
+    protected $config;
+    protected $userRepo;
+    protected $user;
+    protected $enabled;
+
+    /**
+     * Saml2Service constructor.
+     */
+    public function __construct(UserRepo $userRepo, User $user)
+    {
+        $this->config = config('saml2');
+        $this->userRepo = $userRepo;
+        $this->user = $user;
+        $this->enabled = config('saml2.enabled') === true;
+    }
+
+    /**
+     * Initiate a login flow.
+     * @throws Error
+     */
+    public function login(): array
+    {
+        $toolKit = $this->getToolkit();
+        $returnRoute = url('/saml2/acs');
+        return [
+            'url' => $toolKit->login($returnRoute, [], false, false, true),
+            'id' => $toolKit->getLastRequestID(),
+        ];
+    }
+
+    /**
+     * Initiate a logout flow.
+     * @throws Error
+     */
+    public function logout(): array
+    {
+        $toolKit = $this->getToolkit();
+        $returnRoute = url('/');
+
+        try {
+            $url = $toolKit->logout($returnRoute, [], null, null, true);
+            $id = $toolKit->getLastRequestID();
+        } catch (Error $error) {
+            if ($error->getCode() !== Error::SAML_SINGLE_LOGOUT_NOT_SUPPORTED) {
+                throw $error;
+            }
+
+            $this->actionLogout();
+            $url = '/';
+            $id = null;
+        }
+
+        return ['url' => $url, 'id' => $id];
+    }
+
+    /**
+     * Process the ACS response from the idp and return the
+     * matching, or new if registration active, user matched to the idp.
+     * Returns null if not authenticated.
+     * @throws Error
+     * @throws SamlException
+     * @throws ValidationError
+     * @throws JsonDebugException
+     */
+    public function processAcsResponse(?string $requestId): ?User
+    {
+        $toolkit = $this->getToolkit();
+        $toolkit->processResponse($requestId);
+        $errors = $toolkit->getErrors();
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid ACS Response: '.implode(', ', $errors)
+            );
+        }
+
+        if (!$toolkit->isAuthenticated()) {
+            return null;
+        }
+
+        $attrs = $toolkit->getAttributes();
+        $id = $toolkit->getNameId();
+
+        return $this->processLoginCallback($id, $attrs);
+    }
+
+    /**
+     * Process a response for the single logout service.
+     * @throws Error
+     */
+    public function processSlsResponse(?string $requestId): ?string
+    {
+        $toolkit = $this->getToolkit();
+        $redirect = $toolkit->processSLO(true, $requestId, false, null, true);
+
+        $errors = $toolkit->getErrors();
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid SLS Response: '.implode(', ', $errors)
+            );
+        }
+
+        $this->actionLogout();
+        return $redirect;
+    }
+
+    /**
+     * Do the required actions to log a user out.
+     */
+    protected function actionLogout()
+    {
+        auth()->logout();
+        session()->invalidate();
+    }
+
+    /**
+     * Get the metadata for this service provider.
+     * @throws Error
+     */
+    public function metadata(): string
+    {
+        $toolKit = $this->getToolkit();
+        $settings = $toolKit->getSettings();
+        $metadata = $settings->getSPMetadata();
+        $errors = $settings->validateMetadata($metadata);
+
+        if (!empty($errors)) {
+            throw new Error(
+                'Invalid SP metadata: '.implode(', ', $errors),
+                Error::METADATA_SP_INVALID
+            );
+        }
+
+        return $metadata;
+    }
+
+    /**
+     * Load the underlying Onelogin SAML2 toolkit.
+     * @throws Error
+     * @throws Exception
+     */
+    protected function getToolkit(): Auth
+    {
+        $settings = $this->config['onelogin'];
+        $overrides = $this->config['onelogin_overrides'] ?? [];
+
+        if ($overrides && is_string($overrides)) {
+            $overrides = json_decode($overrides, true);
+        }
+
+        $metaDataSettings = [];
+        if ($this->config['autoload_from_metadata']) {
+            $metaDataSettings = IdPMetadataParser::parseRemoteXML($settings['idp']['entityId']);
+        }
+
+        $spSettings = $this->loadOneloginServiceProviderDetails();
+        $settings = array_replace_recursive($settings, $spSettings, $metaDataSettings, $overrides);
+        return new Auth($settings);
+    }
+
+    /**
+     * Load dynamic service provider options required by the onelogin toolkit.
+     */
+    protected function loadOneloginServiceProviderDetails(): array
+    {
+        $spDetails = [
+            'entityId' => url('/saml2/metadata'),
+            'assertionConsumerService' => [
+                'url' => url('/saml2/acs'),
+            ],
+            'singleLogoutService' => [
+                'url' => url('/saml2/sls')
+            ],
+        ];
+
+        return [
+            'baseurl' => url('/saml2'),
+            'sp' => $spDetails
+        ];
+    }
+
+    /**
+     * Check if groups should be synced.
+     */
+    protected function shouldSyncGroups(): bool
+    {
+        return $this->enabled && $this->config['user_to_groups'] !== false;
+    }
+
+    /**
+     * Calculate the display name
+     */
+    protected function getUserDisplayName(array $samlAttributes, string $defaultValue): string
+    {
+        $displayNameAttr = $this->config['display_name_attributes'];
+
+        $displayName = [];
+        foreach ($displayNameAttr as $dnAttr) {
+            $dnComponent = $this->getSamlResponseAttribute($samlAttributes, $dnAttr, null);
+            if ($dnComponent !== null) {
+                $displayName[] = $dnComponent;
+            }
+        }
+
+        if (count($displayName) == 0) {
+            $displayName = $defaultValue;
+        } else {
+            $displayName = implode(' ', $displayName);
+        }
+
+        return $displayName;
+    }
+
+    /**
+     * Get the value to use as the external id saved in BookStack
+     * used to link the user to an existing BookStack DB user.
+     */
+    protected function getExternalId(array $samlAttributes, string $defaultValue)
+    {
+        $userNameAttr = $this->config['external_id_attribute'];
+        if ($userNameAttr === null) {
+            return $defaultValue;
+        }
+
+        return $this->getSamlResponseAttribute($samlAttributes, $userNameAttr, $defaultValue);
+    }
+
+    /**
+     * Extract the details of a user from a SAML response.
+     */
+    public function getUserDetails(string $samlID, $samlAttributes): array
+    {
+        $emailAttr = $this->config['email_attribute'];
+        $externalId = $this->getExternalId($samlAttributes, $samlID);
+
+        $defaultEmail = filter_var($samlID, FILTER_VALIDATE_EMAIL) ? $samlID : null;
+        $email = $this->getSamlResponseAttribute($samlAttributes, $emailAttr, $defaultEmail);
+
+        return [
+            'external_id' => $externalId,
+            'name' => $this->getUserDisplayName($samlAttributes, $externalId),
+            'email' => $email,
+            'saml_id' => $samlID,
+        ];
+    }
+
+    /**
+     * Get the groups a user is a part of from the SAML response.
+     */
+    public function getUserGroups(array $samlAttributes): array
+    {
+        $groupsAttr = $this->config['group_attribute'];
+        $userGroups = $samlAttributes[$groupsAttr] ?? null;
+
+        if (!is_array($userGroups)) {
+            $userGroups = [];
+        }
+
+        return $userGroups;
+    }
+
+    /**
+     *  For an array of strings, return a default for an empty array,
+     *  a string for an array with one element and the full array for
+     *  more than one element.
+     */
+    protected function simplifyValue(array $data, $defaultValue)
+    {
+        switch (count($data)) {
+            case 0:
+                $data = $defaultValue;
+                break;
+            case 1:
+                $data = $data[0];
+                break;
+        }
+        return $data;
+    }
+
+    /**
+     * Get a property from an SAML response.
+     * Handles properties potentially being an array.
+     */
+    protected function getSamlResponseAttribute(array $samlAttributes, string $propertyKey, $defaultValue)
+    {
+        if (isset($samlAttributes[$propertyKey])) {
+            return $this->simplifyValue($samlAttributes[$propertyKey], $defaultValue);
+        }
+
+        return $defaultValue;
+    }
+
+    /**
+     *  Register a user that is authenticated but not already registered.
+     */
+    protected function registerUser(array $userDetails): User
+    {
+        // Create an array of the user data to create a new user instance
+        $userData = [
+            'name' => $userDetails['name'],
+            'email' => $userDetails['email'],
+            'password' => Str::random(32),
+            'external_auth_id' => $userDetails['external_id'],
+            'email_confirmed' => true,
+        ];
+
+        $existingUser = $this->user->newQuery()->where('email', '=', $userDetails['email'])->first();
+        if ($existingUser) {
+            throw new SamlException(trans('errors.saml_email_exists', ['email' => $userDetails['email']]));
+        }
+
+        $user = $this->user->forceCreate($userData);
+        $this->userRepo->attachDefaultRole($user);
+        $this->userRepo->downloadAndAssignUserAvatar($user);
+        return $user;
+    }
+
+    /**
+     * Get the user from the database for the specified details.
+     */
+    protected function getOrRegisterUser(array $userDetails): ?User
+    {
+        $isRegisterEnabled = $this->config['auto_register'] === true;
+        $user = $this->user
+          ->where('external_auth_id', $userDetails['external_id'])
+          ->first();
+
+        if ($user === null && $isRegisterEnabled) {
+            $user = $this->registerUser($userDetails);
+        }
+
+        return $user;
+    }
+
+    /**
+     * Process the SAML response for a user. Login the user when
+     * they exist, optionally registering them automatically.
+     * @throws SamlException
+     * @throws JsonDebugException
+     */
+    public function processLoginCallback(string $samlID, array $samlAttributes): User
+    {
+        $userDetails = $this->getUserDetails($samlID, $samlAttributes);
+        $isLoggedIn = auth()->check();
+
+        if ($this->config['dump_user_details']) {
+            throw new JsonDebugException([
+                'id_from_idp' => $samlID,
+                'attrs_from_idp' => $samlAttributes,
+                'attrs_after_parsing' => $userDetails,
+            ]);
+        }
+
+        if ($userDetails['email'] === null) {
+            throw new SamlException(trans('errors.saml_no_email_address'));
+        }
+
+        if ($isLoggedIn) {
+            throw new SamlException(trans('errors.saml_already_logged_in'), '/login');
+        }
+
+        $user = $this->getOrRegisterUser($userDetails);
+        if ($user === null) {
+            throw new SamlException(trans('errors.saml_user_not_registered', ['name' => $userDetails['external_id']]), '/login');
+        }
+
+        if ($this->shouldSyncGroups()) {
+            $groups = $this->getUserGroups($samlAttributes);
+            $this->syncWithGroups($user, $groups);
+        }
+
+        auth()->login($user);
+        return $user;
+    }
+}
index 9c8d1a81f43ebb8f30d66807a14349cab2882a39..bc5b7a4d5dea27497be83bf37acbd67b407a22bd 100644 (file)
@@ -137,7 +137,7 @@ class SocialAuthService
 
         // Otherwise let the user know this social account is not used by anyone.
         $message = trans('errors.social_account_not_used', ['socialAccount' => $titleCaseDriver]);
-        if (setting('registration-enabled')) {
+        if (setting('registration-enabled') && config('auth.method') !== 'ldap') {
             $message .= trans('errors.social_account_register_instructions', ['socialAccount' => $titleCaseDriver]);
         }
         
index 712f5299b7b1d2ff5485179621aeddf9a1c5efbd..3342ef5a8cadfd8ba9701ebf254f8bd4a6cb1f72 100644 (file)
@@ -4,6 +4,13 @@ use BookStack\Auth\Permissions\JointPermission;
 use BookStack\Auth\Permissions\RolePermission;
 use BookStack\Model;
 
+/**
+ * Class Role
+ * @property string $display_name
+ * @property string $description
+ * @property string $external_auth_id
+ * @package BookStack\Auth
+ */
 class Role extends Model
 {
 
diff --git a/app/Config/saml2.php b/app/Config/saml2.php
new file mode 100644 (file)
index 0000000..2f2ad14
--- /dev/null
@@ -0,0 +1,148 @@
+<?php
+
+return [
+
+    // Display name, shown to users, for SAML2 option
+    'name' => env('SAML2_NAME', 'SSO'),
+    // Toggle whether the SAML2 option is active
+    'enabled' => env('SAML2_ENABLED', false),
+    // Enable registration via SAML2 authentication
+    'auto_register' => env('SAML2_AUTO_REGISTER', true),
+
+    // Dump user details after a login request for debugging purposes
+    'dump_user_details' => env('SAML2_DUMP_USER_DETAILS', false),
+
+    // Attribute, within a SAML response, to find the user's email address
+    'email_attribute' => env('SAML2_EMAIL_ATTRIBUTE', 'email'),
+    // Attribute, within a SAML response, to find the user's display name
+    'display_name_attributes' => explode('|', env('SAML2_DISPLAY_NAME_ATTRIBUTES', 'username')),
+    // Attribute, within a SAML response, to use to connect a BookStack user to the SAML user.
+    'external_id_attribute' => env('SAML2_EXTERNAL_ID_ATTRIBUTE', null),
+
+    // Group sync options
+    // Enable syncing, upon login, of SAML2 groups to BookStack groups
+    'user_to_groups' => env('SAML2_USER_TO_GROUPS', false),
+    // Attribute, within a SAML response, to find group names on
+    'group_attribute' => env('SAML2_GROUP_ATTRIBUTE', 'group'),
+    // When syncing groups, remove any groups that no longer match. Otherwise sync only adds new groups.
+    'remove_from_groups' => env('SAML2_REMOVE_FROM_GROUPS', false),
+
+    // Autoload IDP details from the metadata endpoint
+    'autoload_from_metadata' => env('SAML2_AUTOLOAD_METADATA', false),
+
+    // Overrides, in JSON format, to the configuration passed to underlying onelogin library.
+    'onelogin_overrides' => env('SAML2_ONELOGIN_OVERRIDES', null),
+
+
+    'onelogin' => [
+        // If 'strict' is True, then the PHP Toolkit will reject unsigned
+        // or unencrypted messages if it expects them signed or encrypted
+        // Also will reject the messages if not strictly follow the SAML
+        // standard: Destination, NameId, Conditions ... are validated too.
+        'strict' => true,
+
+        // Enable debug mode (to print errors)
+        'debug' => env('APP_DEBUG', false),
+
+        // Set a BaseURL to be used instead of try to guess
+        // the BaseURL of the view that process the SAML Message.
+        // Ex. http://sp.example.com/
+        //     http://example.com/sp/
+        'baseurl' => null,
+
+        // Service Provider Data that we are deploying
+        'sp' => [
+            // Identifier of the SP entity  (must be a URI)
+            'entityId' => '',
+
+            // Specifies info about where and how the <AuthnResponse> message MUST be
+            // returned to the requester, in this case our SP.
+            'assertionConsumerService' => [
+                // URL Location where the <Response> from the IdP will be returned
+                'url' => '',
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-POST binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
+            ],
+
+            // Specifies info about where and how the <Logout Response> message MUST be
+            // returned to the requester, in this case our SP.
+            'singleLogoutService' => [
+                // URL Location where the <Response> from the IdP will be returned
+                'url' => '',
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+
+            // Specifies constraints on the name identifier to be used to
+            // represent the requested subject.
+            // Take a look on lib/Saml2/Constants.php to see the NameIdFormat supported
+            'NameIDFormat' => 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
+            // Usually x509cert and privateKey of the SP are provided by files placed at
+            // the certs folder. But we can also provide them with the following parameters
+            'x509cert' => '',
+            'privateKey' => '',
+        ],
+        // Identity Provider Data that we want connect with our SP
+        'idp' => [
+            // Identifier of the IdP entity  (must be a URI)
+            'entityId' => env('SAML2_IDP_ENTITYID', null),
+            // SSO endpoint info of the IdP. (Authentication Request protocol)
+            'singleSignOnService' => [
+                // URL Target of the IdP where the SP will send the Authentication Request Message
+                'url' => env('SAML2_IDP_SSO', null),
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+            // SLO endpoint info of the IdP.
+            'singleLogoutService' => [
+                // URL Location of the IdP where the SP will send the SLO Request
+                'url' => env('SAML2_IDP_SLO', null),
+                // URL location of the IdP where the SP will send the SLO Response (ResponseLocation)
+                // if not set, url for the SLO Request will be used
+                'responseUrl' => '',
+                // SAML protocol binding to be used when returning the <Response>
+                // message.  Onelogin Toolkit supports for this endpoint the
+                // HTTP-Redirect binding only
+                'binding' => 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect',
+            ],
+            // Public x509 certificate of the IdP
+            'x509cert' => env('SAML2_IDP_x509', null),
+            /*
+             *  Instead of use the whole x509cert you can use a fingerprint in
+             *  order to validate the SAMLResponse, but we don't recommend to use
+             *  that method on production since is exploitable by a collision
+             *  attack.
+             *  (openssl x509 -noout -fingerprint -in "idp.crt" to generate it,
+             *   or add for example the -sha256 , -sha384 or -sha512 parameter)
+             *
+             *  If a fingerprint is provided, then the certFingerprintAlgorithm is required in order to
+             *  let the toolkit know which Algorithm was used. Possible values: sha1, sha256, sha384 or sha512
+             *  'sha1' is the default value.
+             */
+            // 'certFingerprint' => '',
+            // 'certFingerprintAlgorithm' => 'sha1',
+            /* In some scenarios the IdP uses different certificates for
+             * signing/encryption, or is under key rollover phase and more
+             * than one certificate is published on IdP metadata.
+             * In order to handle that the toolkit offers that parameter.
+             * (when used, 'x509cert' and 'certFingerprint' values are
+             * ignored).
+             */
+            // 'x509certMulti' => array(
+            //      'signing' => array(
+            //          0 => '<cert1-string>',
+            //      ),
+            //      'encryption' => array(
+            //          0 => '<cert2-string>',
+            //      )
+            // ),
+        ],
+    ],
+
+];
index 923015f6e62815e66b388a21f6e9d4b0b2282388..a0bdd078a9f01318d2c335d341ba9c14a29c2c12 100644 (file)
@@ -123,6 +123,7 @@ return [
         'base_dn' => env('LDAP_BASE_DN', false),
         'user_filter' => env('LDAP_USER_FILTER', '(&(uid=${user}))'),
         'version' => env('LDAP_VERSION', false),
+        'id_attribute' => env('LDAP_ID_ATTRIBUTE', 'uid'),
         'email_attribute' => env('LDAP_EMAIL_ATTRIBUTE', 'mail'),
         'display_name_attribute' => env('LDAP_DISPLAY_NAME_ATTRIBUTE', 'cn'),
         'follow_referrals' => env('LDAP_FOLLOW_REFERRALS', false),
@@ -130,6 +131,6 @@ return [
         'group_attribute' => env('LDAP_GROUP_ATTRIBUTE', 'memberOf'),
         'remove_from_groups' => env('LDAP_REMOVE_FROM_GROUPS', false),
         'tls_insecure' => env('LDAP_TLS_INSECURE', false),
-    ]
+    ],
 
 ];
index c6080df1db6df81b434a935dde272c0f038c5c0a..d84c0c2641397700da7b551414122c66e88fa24b 100644 (file)
@@ -16,6 +16,11 @@ return [
     'app-editor'           => 'wysiwyg',
     'app-color'            => '#206ea7',
     'app-color-light'      => 'rgba(32,110,167,0.15)',
+    'bookshelf-color'      => '#a94747',
+    'book-color'           => '#077b70',
+    'chapter-color'        => '#af4d0d',
+    'page-color'           => '#206ea7',
+    'page-draft-color'     => '#7e50b1',
     'app-custom-head'      => false,
     'registration-enabled' => false,
 
diff --git a/app/Console/Commands/CopyShelfPermissions.php b/app/Console/Commands/CopyShelfPermissions.php
new file mode 100644 (file)
index 0000000..d9a1c1d
--- /dev/null
@@ -0,0 +1,88 @@
+<?php
+
+namespace BookStack\Console\Commands;
+
+use BookStack\Entities\Bookshelf;
+use BookStack\Entities\Repos\BookshelfRepo;
+use Illuminate\Console\Command;
+
+class CopyShelfPermissions extends Command
+{
+    /**
+     * The name and signature of the console command.
+     *
+     * @var string
+     */
+    protected $signature = 'bookstack:copy-shelf-permissions
+                            {--a|all : Perform for all shelves in the system}
+                            {--s|slug= : The slug for a shelf to target}
+                            ';
+
+    /**
+     * The console command description.
+     *
+     * @var string
+     */
+    protected $description = 'Copy shelf permissions to all child books.';
+
+    /**
+     * @var BookshelfRepo
+     */
+    protected $bookshelfRepo;
+
+    /**
+     * Create a new command instance.
+     *
+     * @return void
+     */
+    public function __construct(BookshelfRepo $repo)
+    {
+        $this->bookshelfRepo = $repo;
+        parent::__construct();
+    }
+
+    /**
+     * Execute the console command.
+     *
+     * @return mixed
+     */
+    public function handle()
+    {
+        $shelfSlug = $this->option('slug');
+        $cascadeAll = $this->option('all');
+        $shelves = null;
+
+        if (!$cascadeAll && !$shelfSlug) {
+            $this->error('Either a --slug or --all option must be provided.');
+            return;
+        }
+
+        if ($cascadeAll) {
+            $continue = $this->confirm(
+                'Permission settings for all shelves will be cascaded. '.
+                        'Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. '.
+                        'Are you sure you want to proceed?'
+            );
+
+            if (!$continue && !$this->hasOption('no-interaction')) {
+                return;
+            }
+
+            $shelves = Bookshelf::query()->get(['id', 'restricted']);
+        }
+
+        if ($shelfSlug) {
+            $shelves = Bookshelf::query()->where('slug', '=', $shelfSlug)->get(['id', 'restricted']);
+            if ($shelves->count() === 0) {
+                $this->info('No shelves found with the given slug.');
+            }
+        }
+
+        foreach ($shelves as $shelf) {
+            $this->bookshelfRepo->copyDownPermissions($shelf, false);
+            $this->info('Copied permissions for shelf [' . $shelf->id . ']');
+        }
+
+        $this->info('Permissions copied for ' . $shelves->count() . ' shelves.');
+    }
+}
index ab4a518054eb89ac76c47855bd0d811f09792ea7..03b54f009a5ae6215656b39cdf2cb023db6513e3 100644 (file)
@@ -139,15 +139,15 @@ class BookshelfRepo
     /**
      * Copy down the permissions of the given shelf to all child books.
      */
-    public function copyDownPermissions(Bookshelf $shelf): int
+    public function copyDownPermissions(Bookshelf $shelf, $checkUserPermissions = true): int
     {
         $shelfPermissions = $shelf->permissions()->get(['role_id', 'action'])->toArray();
-        $shelfBooks = $shelf->books()->get();
+        $shelfBooks = $shelf->books()->get(['id', 'restricted']);
         $updatedBookCount = 0;
 
         /** @var Book $book */
         foreach ($shelfBooks as $book) {
-            if (!userCan('restrictions-manage', $book)) {
+            if ($checkUserPermissions && !userCan('restrictions-manage', $book)) {
                 continue;
             }
             $book->permissions()->delete();
diff --git a/app/Exceptions/JsonDebugException.php b/app/Exceptions/JsonDebugException.php
new file mode 100644 (file)
index 0000000..6314533
--- /dev/null
@@ -0,0 +1,25 @@
+<?php namespace BookStack\Exceptions;
+
+use Exception;
+
+class JsonDebugException extends Exception
+{
+
+    protected $data;
+
+    /**
+     * JsonDebugException constructor.
+     */
+    public function __construct($data)
+    {
+        $this->data = $data;
+    }
+
+    /**
+     * Covert this exception into a response.
+     */
+    public function render()
+    {
+        return response()->json($this->data);
+    }
+}
\ No newline at end of file
diff --git a/app/Exceptions/SamlException.php b/app/Exceptions/SamlException.php
new file mode 100644 (file)
index 0000000..13db23f
--- /dev/null
@@ -0,0 +1,6 @@
+<?php namespace BookStack\Exceptions;
+
+class SamlException extends NotifyException
+{
+
+}
index 2b51ab99765d069470f66252b67bf514ff7f8e5a..b1d22d57e1f892a7068f66dd90c6cd0acceb7715 100644 (file)
@@ -119,6 +119,7 @@ class LoginController extends Controller
     {
         $socialDrivers = $this->socialAuthService->getActiveDrivers();
         $authMethod = config('auth.method');
+        $samlEnabled = config('saml2.enabled') === true;
 
         if ($request->has('email')) {
             session()->flashInput([
@@ -127,7 +128,11 @@ class LoginController extends Controller
             ]);
         }
 
-        return view('auth.login', ['socialDrivers' => $socialDrivers, 'authMethod' => $authMethod]);
+        return view('auth.login', [
+          'socialDrivers' => $socialDrivers,
+          'authMethod' => $authMethod,
+          'samlEnabled' => $samlEnabled,
+        ]);
     }
 
     /**
@@ -141,4 +146,23 @@ class LoginController extends Controller
         session()->put('social-callback', 'login');
         return $this->socialAuthService->startLogIn($socialDriver);
     }
+
+    /**
+     * Log the user out of the application.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function logout(Request $request)
+    {
+        if (config('saml2.enabled') && session()->get('last_login_type') === 'saml2') {
+            return redirect('/saml2/logout');
+        }
+
+        $this->guard()->logout();
+
+        $request->session()->invalidate();
+
+        return $this->loggedOut($request) ?: redirect('/');
+    }
 }
index 304d3bed2e69999b381e0a8e1b4cb6a22790f492..8e4dd57c3bd9ffbf4dcdc546e57f67787a98896b 100644 (file)
@@ -89,7 +89,7 @@ class RegisterController extends Controller
      */
     protected function checkRegistrationAllowed()
     {
-        if (!setting('registration-enabled')) {
+        if (!setting('registration-enabled') || config('auth.method') === 'ldap') {
             throw new UserRegistrationException(trans('auth.registrations_disabled'), '/login');
         }
     }
@@ -103,7 +103,11 @@ class RegisterController extends Controller
     {
         $this->checkRegistrationAllowed();
         $socialDrivers = $this->socialAuthService->getActiveDrivers();
-        return view('auth.register', ['socialDrivers' => $socialDrivers]);
+        $samlEnabled = (config('saml2.enabled') === true) && (config('saml2.auto_register') === true);
+        return view('auth.register', [
+            'socialDrivers' => $socialDrivers,
+            'samlEnabled' => $samlEnabled,
+        ]);
     }
 
     /**
diff --git a/app/Http/Controllers/Auth/Saml2Controller.php b/app/Http/Controllers/Auth/Saml2Controller.php
new file mode 100644 (file)
index 0000000..8638941
--- /dev/null
@@ -0,0 +1,96 @@
+<?php
+
+namespace BookStack\Http\Controllers\Auth;
+
+use BookStack\Auth\Access\Saml2Service;
+use BookStack\Http\Controllers\Controller;
+
+class Saml2Controller extends Controller
+{
+
+    protected $samlService;
+
+    /**
+     * Saml2Controller constructor.
+     */
+    public function __construct(Saml2Service $samlService)
+    {
+        parent::__construct();
+        $this->samlService = $samlService;
+
+        // SAML2 access middleware
+        $this->middleware(function ($request, $next) {
+            if (!config('saml2.enabled')) {
+                $this->showPermissionError();
+            }
+
+            return $next($request);
+        });
+    }
+
+    /**
+     * Start the login flow via SAML2.
+     */
+    public function login()
+    {
+        $loginDetails = $this->samlService->login();
+        session()->flash('saml2_request_id', $loginDetails['id']);
+
+        return redirect($loginDetails['url']);
+    }
+
+    /**
+     * Start the logout flow via SAML2.
+     */
+    public function logout()
+    {
+        $logoutDetails = $this->samlService->logout();
+
+        if ($logoutDetails['id']) {
+            session()->flash('saml2_logout_request_id', $logoutDetails['id']);
+        }
+
+        return redirect($logoutDetails['url']);
+    }
+
+    /*
+     * Get the metadata for this SAML2 service provider.
+     */
+    public function metadata()
+    {
+        $metaData = $this->samlService->metadata();
+        return response()->make($metaData, 200, [
+            'Content-Type' => 'text/xml'
+        ]);
+    }
+
+    /**
+     * Single logout service.
+     * Handle logout requests and responses.
+     */
+    public function sls()
+    {
+        $requestId = session()->pull('saml2_logout_request_id', null);
+        $redirect = $this->samlService->processSlsResponse($requestId) ?? '/';
+        return redirect($redirect);
+    }
+
+    /**
+     * Assertion Consumer Service.
+     * Processes the SAML response from the IDP.
+     */
+    public function acs()
+    {
+        $requestId = session()->pull('saml2_request_id', null);
+
+        $user = $this->samlService->processAcsResponse($requestId);
+        if ($user === null) {
+            $this->showErrorNotification(trans('errors.saml_fail_authed', ['system' => config('saml2.name')]));
+            return redirect('/login');
+        }
+
+        session()->put('last_login_type', 'saml2');
+        return redirect()->intended();
+    }
+
+}
index 630f888ed3e9dc0d16da96b622a03278b527cfb9..b216c19a8e7689a2e4797f12c9da8e2c92512c0b 100644 (file)
@@ -304,11 +304,12 @@ class PageController extends Controller
         $this->checkOwnablePermission('page-delete', $page);
 
         $book = $page->book;
+        $parent = $page->chapter ?? $book;
         $this->pageRepo->destroy($page);
         Activity::addMessage('page_delete', $page->name, $book->id);
 
         $this->showSuccessNotification(trans('entities.pages_delete_success'));
-        return redirect($book->getUrl());
+        return redirect($parent->getUrl());
     }
 
     /**
index 1a29a2b1d121f35f56dfd9e2eeb5611c84c4b42a..bdeb265540a9bde5c6d53f6ffef8b5e8f1ca3711 100644 (file)
@@ -19,6 +19,6 @@ class VerifyCsrfToken extends Middleware
      * @var array
      */
     protected $except = [
-        //
+        'saml2/*'
     ];
 }
index dede8fcc418634db821a3d6948b84203e872fd28..1c053b3848ea779d480adea01834dce0059d629d 100644 (file)
@@ -98,12 +98,6 @@ class SettingService
      */
     protected function getValueFromStore($key, $default)
     {
-        // Check for an overriding value
-        $overrideValue = $this->getOverrideValue($key);
-        if ($overrideValue !== null) {
-            return $overrideValue;
-        }
-
         // Check the cache
         $cacheKey = $this->cachePrefix . $key;
         $cacheVal = $this->cache->get($cacheKey, null);
@@ -255,20 +249,4 @@ class SettingService
     {
         return $this->setting->where('setting_key', '=', $key)->first();
     }
-
-
-    /**
-     * Returns an override value for a setting based on certain app conditions.
-     * Used where certain configuration options overrule others.
-     * Returns null if no override value is available.
-     * @param $key
-     * @return bool|null
-     */
-    protected function getOverrideValue($key)
-    {
-        if ($key === 'registration-enabled' && config('auth.method') === 'ldap') {
-            return false;
-        }
-        return null;
-    }
 }
index da0b7d379eddd2fb4a227dd071af248c2ba44d07..01b65f882698933b54f66ba51dcee3f885aaf5cc 100644 (file)
@@ -2,6 +2,8 @@
 
 use BookStack\Auth\Permissions\PermissionService;
 use BookStack\Entities\Page;
+use BookStack\Exceptions\ImageUploadException;
+use Exception;
 use Illuminate\Database\Eloquent\Builder;
 use Symfony\Component\HttpFoundation\File\UploadedFile;
 
@@ -15,10 +17,6 @@ class ImageRepo
 
     /**
      * ImageRepo constructor.
-     * @param Image $image
-     * @param ImageService $imageService
-     * @param \BookStack\Auth\Permissions\PermissionService $permissionService
-     * @param \BookStack\Entities\Page $page
      */
     public function __construct(
         Image $image,
@@ -35,10 +33,8 @@ class ImageRepo
 
     /**
      * Get an image with the given id.
-     * @param $id
-     * @return Image
      */
-    public function getById($id)
+    public function getById($id): Image
     {
         return $this->image->findOrFail($id);
     }
@@ -46,13 +42,8 @@ class ImageRepo
     /**
      * Execute a paginated query, returning in a standard format.
      * Also runs the query through the restriction system.
-     * @param $query
-     * @param int $page
-     * @param int $pageSize
-     * @param bool $filterOnPage
-     * @return array
      */
-    private function returnPaginated($query, $page = 1, $pageSize = 24)
+    private function returnPaginated($query, $page = 1, $pageSize = 24): array
     {
         $images = $query->orderBy('created_at', 'desc')->skip($pageSize * ($page - 1))->take($pageSize + 1)->get();
         $hasMore = count($images) > $pageSize;
@@ -71,13 +62,6 @@ class ImageRepo
     /**
      * Fetch a list of images in a paginated format, filtered by image type.
      * Can be filtered by uploaded to and also by name.
-     * @param string $type
-     * @param int $page
-     * @param int $pageSize
-     * @param int $uploadedTo
-     * @param string|null $search
-     * @param callable|null $whereClause
-     * @return array
      */
     public function getPaginatedByType(
         string $type,
@@ -86,7 +70,8 @@ class ImageRepo
         int $uploadedTo = null,
         string $search = null,
         callable $whereClause = null
-    ) {
+    ): array
+    {
         $imageQuery = $this->image->newQuery()->where('type', '=', strtolower($type));
 
         if ($uploadedTo !== null) {
@@ -109,13 +94,6 @@ class ImageRepo
 
     /**
      * Get paginated gallery images within a specific page or book.
-     * @param string $type
-     * @param string $filterType
-     * @param int $page
-     * @param int $pageSize
-     * @param int|null $uploadedTo
-     * @param string|null $search
-     * @return array
      */
     public function getEntityFiltered(
         string $type,
@@ -124,7 +102,8 @@ class ImageRepo
         int $pageSize = 24,
         int $uploadedTo = null,
         string $search = null
-    ) {
+    ): array
+    {
         $contextPage = $this->page->findOrFail($uploadedTo);
         $parentFilter = null;
 
@@ -144,16 +123,9 @@ class ImageRepo
 
     /**
      * Save a new image into storage and return the new image.
-     * @param UploadedFile $uploadFile
-     * @param string $type
-     * @param int $uploadedTo
-     * @param int|null $resizeWidth
-     * @param int|null $resizeHeight
-     * @param bool $keepRatio
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
+     * @throws ImageUploadException
      */
-    public function saveNew(UploadedFile $uploadFile, $type, $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true)
+    public function saveNew(UploadedFile $uploadFile, string $type, int $uploadedTo = 0, int $resizeWidth = null, int $resizeHeight = null, bool $keepRatio = true): Image
     {
         $image = $this->imageService->saveNewFromUpload($uploadFile, $type, $uploadedTo, $resizeWidth, $resizeHeight, $keepRatio);
         $this->loadThumbs($image);
@@ -161,29 +133,22 @@ class ImageRepo
     }
 
     /**
-     * Save a drawing the the database;
-     * @param string $base64Uri
-     * @param int $uploadedTo
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
+     * Save a drawing the the database.
+     * @throws ImageUploadException
      */
-    public function saveDrawing(string $base64Uri, int $uploadedTo)
+    public function saveDrawing(string $base64Uri, int $uploadedTo): Image
     {
         $name = 'Drawing-' . user()->getShortName(40) . '-' . strval(time()) . '.png';
-        $image = $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawio', $uploadedTo);
-        return $image;
+        return $this->imageService->saveNewFromBase64Uri($base64Uri, $name, 'drawio', $uploadedTo);
     }
 
 
     /**
      * Update the details of an image via an array of properties.
-     * @param Image $image
-     * @param array $updateDetails
-     * @return Image
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws ImageUploadException
+     * @throws Exception
      */
-    public function updateImageDetails(Image $image, $updateDetails)
+    public function updateImageDetails(Image $image, $updateDetails): Image
     {
         $image->fill($updateDetails);
         $image->save();
@@ -191,14 +156,11 @@ class ImageRepo
         return $image;
     }
 
-
     /**
      * Destroys an Image object along with its revisions, files and thumbnails.
-     * @param Image $image
-     * @return bool
-     * @throws \Exception
+     * @throws Exception
      */
-    public function destroyImage(Image $image = null)
+    public function destroyImage(Image $image = null): bool
     {
         if ($image) {
             $this->imageService->destroy($image);
@@ -208,8 +170,7 @@ class ImageRepo
 
     /**
      * Destroy all images of a certain type.
-     * @param string $imageType
-     * @throws \Exception
+     * @throws Exception
      */
     public function destroyByType(string $imageType)
     {
@@ -222,9 +183,7 @@ class ImageRepo
 
     /**
      * Load thumbnails onto an image object.
-     * @param Image $image
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws Exception
      */
     protected function loadThumbs(Image $image)
     {
@@ -238,42 +197,33 @@ class ImageRepo
      * Get the thumbnail for an image.
      * If $keepRatio is true only the width will be used.
      * Checks the cache then storage to avoid creating / accessing the filesystem on every check.
-     * @param Image $image
-     * @param int $width
-     * @param int $height
-     * @param bool $keepRatio
-     * @return string
-     * @throws \BookStack\Exceptions\ImageUploadException
-     * @throws \Exception
+     * @throws Exception
      */
-    protected function getThumbnail(Image $image, $width = 220, $height = 220, $keepRatio = false)
+    protected function getThumbnail(Image $image, ?int $width = 220, ?int $height = 220, bool $keepRatio = false): ?string
     {
         try {
             return $this->imageService->getThumbnail($image, $width, $height, $keepRatio);
-        } catch (\Exception $exception) {
+        } catch (Exception $exception) {
             return null;
         }
     }
 
     /**
      * Get the raw image data from an Image.
-     * @param Image $image
-     * @return null|string
      */
-    public function getImageData(Image $image)
+    public function getImageData(Image $image): ?string
     {
         try {
             return $this->imageService->getImageData($image);
-        } catch (\Exception $exception) {
+        } catch (Exception $exception) {
             return null;
         }
     }
 
     /**
      * Get the validation rules for image files.
-     * @return string
      */
-    public function getImageValidationRules()
+    public function getImageValidationRules(): string
     {
         return 'image_extension|no_double_extension|mimes:jpeg,png,gif,bmp,webp,tiff';
     }
index e7668471bd64c174c858eef0ac6c9c36a16e8880..756149fe7a1bacdc2f11a2f4ee3672ceb50c8676 100644 (file)
@@ -254,7 +254,16 @@ class ImageService extends UploadService
         } else {
             $thumb->fit($width, $height);
         }
-        return (string)$thumb->encode();
+
+        $thumbData = (string)$thumb->encode();
+
+        // Use original image data if we're keeping the ratio
+        // and the resizing does not save any space.
+        if ($keepRatio && strlen($thumbData) > strlen($imageData)) {
+            return $imageData;
+        }
+
+        return $thumbData;
     }
 
     /**
index a8b9456a1856c01fb2bf65e14ea32de0f7c55a15..98cfa1e2a13352123e12ea2c05da6b758c48b5f6 100644 (file)
@@ -22,6 +22,7 @@
         "laravel/framework": "^6.0",
         "laravel/socialite": "^4.2",
         "league/flysystem-aws-s3-v3": "^1.0",
+        "onelogin/php-saml": "^3.3",
         "predis/predis": "^1.1",
         "socialiteproviders/discord": "^2.0",
         "socialiteproviders/gitlab": "^3.0",
index 3ec106ded4f0c57c9ca3556222dff16874606bc8..346adb47c0aaa21019c27445daeec117ddb6102e 100644 (file)
@@ -1,23 +1,23 @@
 {
     "_readme": [
         "This file locks the dependencies of your project to a known state",
-        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "c156e1738dbab2a57f9a926d9a9a5a6a",
+    "content-hash": "140c7a04a20cef6d7ed8c1fc48257e66",
     "packages": [
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.112.0",
+            "version": "3.117.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "1e21446c6780a3b9b5e4315bd6d4347d2c3381eb"
+                "reference": "3dc81df70f1cdf2842c85915548bffb870c1e1da"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/1e21446c6780a3b9b5e4315bd6d4347d2c3381eb",
-                "reference": "1e21446c6780a3b9b5e4315bd6d4347d2c3381eb",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3dc81df70f1cdf2842c85915548bffb870c1e1da",
+                "reference": "3dc81df70f1cdf2842c85915548bffb870c1e1da",
                 "shasum": ""
             },
             "require": {
@@ -87,7 +87,7 @@
                 "s3",
                 "sdk"
             ],
-            "time": "2019-09-12T18:09:53+00:00"
+            "time": "2019-11-15T19:21:02+00:00"
         },
         {
             "name": "barryvdh/laravel-dompdf",
         },
         {
             "name": "barryvdh/laravel-snappy",
-            "version": "v0.4.5",
+            "version": "v0.4.6",
             "source": {
                 "type": "git",
                 "url": "https://github.com/barryvdh/laravel-snappy.git",
-                "reference": "9be767fc7a082665a84945f36c70b0cbead91ce9"
+                "reference": "94d53c88fa58baa4573c5854663ebc9955f21265"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/9be767fc7a082665a84945f36c70b0cbead91ce9",
-                "reference": "9be767fc7a082665a84945f36c70b0cbead91ce9",
+                "url": "https://api.github.com/repos/barryvdh/laravel-snappy/zipball/94d53c88fa58baa4573c5854663ebc9955f21265",
+                "reference": "94d53c88fa58baa4573c5854663ebc9955f21265",
                 "shasum": ""
             },
             "require": {
-                "illuminate/filesystem": "5.5.x|5.6.x|5.7.x|5.8.x|6.0.*",
-                "illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x|6.0.*",
+                "illuminate/filesystem": "5.5.x|5.6.x|5.7.x|5.8.x|6.*",
+                "illuminate/support": "5.5.x|5.6.x|5.7.x|5.8.x|6.*",
                 "knplabs/knp-snappy": "^1",
                 "php": ">=7"
             },
                 "wkhtmltoimage",
                 "wkhtmltopdf"
             ],
-            "time": "2019-08-30T16:12:23+00:00"
+            "time": "2019-10-02T23:27:09+00:00"
         },
         {
             "name": "cogpowered/finediff",
         },
         {
             "name": "doctrine/cache",
-            "version": "v1.8.0",
+            "version": "1.9.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/cache.git",
-                "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57"
+                "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/cache/zipball/d768d58baee9a4862ca783840eca1b9add7a7f57",
-                "reference": "d768d58baee9a4862ca783840eca1b9add7a7f57",
+                "url": "https://api.github.com/repos/doctrine/cache/zipball/89a5c76c39c292f7798f964ab3c836c3f8192a55",
+                "reference": "89a5c76c39c292f7798f964ab3c836c3f8192a55",
                 "shasum": ""
             },
             "require": {
             },
             "require-dev": {
                 "alcaeus/mongo-php-adapter": "^1.1",
-                "doctrine/coding-standard": "^4.0",
+                "doctrine/coding-standard": "^6.0",
                 "mongodb/mongodb": "^1.1",
                 "phpunit/phpunit": "^7.0",
                 "predis/predis": "~1.0"
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.8.x-dev"
+                    "dev-master": "1.9.x-dev"
                 }
             },
             "autoload": {
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                     "email": "[email protected]"
                 }
             ],
-            "description": "Caching library offering an object-oriented API for many cache backends",
-            "homepage": "https://www.doctrine-project.org",
+            "description": "PHP Doctrine Cache library is a popular cache implementation that supports many different drivers such as redis, memcache, apc, mongodb and others.",
+            "homepage": "https://www.doctrine-project.org/projects/cache.html",
             "keywords": [
+                "abstraction",
+                "apcu",
                 "cache",
-                "caching"
+                "caching",
+                "couchdb",
+                "memcached",
+                "php",
+                "redis",
+                "riak",
+                "xcache"
             ],
-            "time": "2018-08-21T18:01:43+00:00"
+            "time": "2019-11-15T14:31:57+00:00"
         },
         {
             "name": "doctrine/dbal",
-            "version": "v2.9.2",
+            "version": "v2.10.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/dbal.git",
-                "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9"
+                "reference": "0c9a646775ef549eb0a213a4f9bd4381d9b4d934"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/dbal/zipball/22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
-                "reference": "22800bd651c1d8d2a9719e2a3dc46d5108ebfcc9",
+                "url": "https://api.github.com/repos/doctrine/dbal/zipball/0c9a646775ef549eb0a213a4f9bd4381d9b4d934",
+                "reference": "0c9a646775ef549eb0a213a4f9bd4381d9b4d934",
                 "shasum": ""
             },
             "require": {
                 "doctrine/cache": "^1.0",
                 "doctrine/event-manager": "^1.0",
                 "ext-pdo": "*",
-                "php": "^7.1"
+                "php": "^7.2"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^5.0",
-                "jetbrains/phpstorm-stubs": "^2018.1.2",
-                "phpstan/phpstan": "^0.10.1",
-                "phpunit/phpunit": "^7.4",
-                "symfony/console": "^2.0.5|^3.0|^4.0",
-                "symfony/phpunit-bridge": "^3.4.5|^4.0.5"
+                "doctrine/coding-standard": "^6.0",
+                "jetbrains/phpstorm-stubs": "^2019.1",
+                "phpstan/phpstan": "^0.11.3",
+                "phpunit/phpunit": "^8.4.1",
+                "symfony/console": "^2.0.5|^3.0|^4.0|^5.0"
             },
             "suggest": {
                 "symfony/console": "For helpful console commands such as SQL execution and import of files."
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "2.9.x-dev",
+                    "dev-master": "2.10.x-dev",
                     "dev-develop": "3.0.x-dev"
                 }
             },
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
             "keywords": [
                 "abstraction",
                 "database",
+                "db2",
                 "dbal",
+                "mariadb",
+                "mssql",
                 "mysql",
-                "persistence",
+                "oci8",
+                "oracle",
+                "pdo",
                 "pgsql",
-                "php",
-                "queryobject"
+                "postgresql",
+                "queryobject",
+                "sasql",
+                "sql",
+                "sqlanywhere",
+                "sqlite",
+                "sqlserver",
+                "sqlsrv"
             ],
-            "time": "2018-12-31T03:27:51+00:00"
+            "time": "2019-11-03T16:50:43+00:00"
         },
         {
             "name": "doctrine/event-manager",
-            "version": "v1.0.0",
+            "version": "1.1.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/event-manager.git",
-                "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3"
+                "reference": "629572819973f13486371cb611386eb17851e85c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/a520bc093a0170feeb6b14e9d83f3a14452e64b3",
-                "reference": "a520bc093a0170feeb6b14e9d83f3a14452e64b3",
+                "url": "https://api.github.com/repos/doctrine/event-manager/zipball/629572819973f13486371cb611386eb17851e85c",
+                "reference": "629572819973f13486371cb611386eb17851e85c",
                 "shasum": ""
             },
             "require": {
                 "doctrine/common": "<2.9@dev"
             },
             "require-dev": {
-                "doctrine/coding-standard": "^4.0",
+                "doctrine/coding-standard": "^6.0",
                 "phpunit/phpunit": "^7.0"
             },
             "type": "library",
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                     "email": "[email protected]"
                 }
             ],
-            "description": "Doctrine Event Manager component",
+            "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.",
             "homepage": "https://www.doctrine-project.org/projects/event-manager.html",
             "keywords": [
                 "event",
-                "eventdispatcher",
-                "eventmanager"
+                "event dispatcher",
+                "event manager",
+                "event system",
+                "events"
             ],
-            "time": "2018-06-11T11:59:03+00:00"
+            "time": "2019-11-10T09:48:07+00:00"
         },
         {
             "name": "doctrine/inflector",
-            "version": "v1.3.0",
+            "version": "1.3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/inflector.git",
-                "reference": "5527a48b7313d15261292c149e55e26eae771b0a"
+                "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/inflector/zipball/5527a48b7313d15261292c149e55e26eae771b0a",
-                "reference": "5527a48b7313d15261292c149e55e26eae771b0a",
+                "url": "https://api.github.com/repos/doctrine/inflector/zipball/ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
+                "reference": "ec3a55242203ffa6a4b27c58176da97ff0a7aec1",
                 "shasum": ""
             },
             "require": {
                 "MIT"
             ],
             "authors": [
+                {
+                    "name": "Guilherme Blanco",
+                    "email": "[email protected]"
+                },
                 {
                     "name": "Roman Borschel",
                     "email": "[email protected]"
                     "name": "Benjamin Eberlei",
                     "email": "[email protected]"
                 },
-                {
-                    "name": "Guilherme Blanco",
-                    "email": "[email protected]"
-                },
                 {
                     "name": "Jonathan Wage",
                     "email": "[email protected]"
                 "singularize",
                 "string"
             ],
-            "time": "2018-01-09T20:05:19+00:00"
+            "time": "2019-10-30T19:59:35+00:00"
         },
         {
             "name": "doctrine/lexer",
-            "version": "1.1.0",
+            "version": "1.2.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/lexer.git",
-                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea"
+                "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/lexer/zipball/e17f069ede36f7534b95adec71910ed1b49c74ea",
-                "reference": "e17f069ede36f7534b95adec71910ed1b49c74ea",
+                "url": "https://api.github.com/repos/doctrine/lexer/zipball/5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
+                "reference": "5242d66dbeb21a30dd8a3e66bf7a73b66e05e1f6",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.1.x-dev"
+                    "dev-master": "1.2.x-dev"
                 }
             },
             "autoload": {
                 "parser",
                 "php"
             ],
-            "time": "2019-07-30T19:33:28+00:00"
+            "time": "2019-10-30T14:39:59+00:00"
         },
         {
             "name": "dompdf/dompdf",
         },
         {
             "name": "guzzlehttp/guzzle",
-            "version": "6.3.3",
+            "version": "6.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/guzzle.git",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba"
+                "reference": "0895c932405407fd3a7368b6910c09a24d26db11"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba",
-                "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba",
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/0895c932405407fd3a7368b6910c09a24d26db11",
+                "reference": "0895c932405407fd3a7368b6910c09a24d26db11",
                 "shasum": ""
             },
             "require": {
+                "ext-json": "*",
                 "guzzlehttp/promises": "^1.0",
-                "guzzlehttp/psr7": "^1.4",
+                "guzzlehttp/psr7": "^1.6.1",
                 "php": ">=5.5"
             },
             "require-dev": {
                 "ext-curl": "*",
                 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0",
-                "psr/log": "^1.0"
+                "psr/log": "^1.1"
             },
             "suggest": {
                 "psr/log": "Required for using the Log middleware"
                 }
             },
             "autoload": {
-                "files": [
-                    "src/functions_include.php"
-                ],
                 "psr-4": {
                     "GuzzleHttp\\": "src/"
-                }
+                },
+                "files": [
+                    "src/functions_include.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "rest",
                 "web service"
             ],
-            "time": "2018-04-22T15:46:56+00:00"
+            "time": "2019-10-23T15:58:00+00:00"
         },
         {
             "name": "guzzlehttp/promises",
         },
         {
             "name": "intervention/image",
-            "version": "2.5.0",
+            "version": "2.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Intervention/image.git",
-                "reference": "39eaef720d082ecc54c64bf54541c55f10db546d"
+                "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Intervention/image/zipball/39eaef720d082ecc54c64bf54541c55f10db546d",
-                "reference": "39eaef720d082ecc54c64bf54541c55f10db546d",
+                "url": "https://api.github.com/repos/Intervention/image/zipball/abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
+                "reference": "abbf18d5ab8367f96b3205ca3c89fb2fa598c69e",
                 "shasum": ""
             },
             "require": {
                 "thumbnail",
                 "watermark"
             ],
-            "time": "2019-06-24T14:06:31+00:00"
+            "time": "2019-11-02T09:15:47+00:00"
         },
         {
             "name": "knplabs/knp-snappy",
         },
         {
             "name": "laravel/framework",
-            "version": "v6.0.3",
+            "version": "v6.5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/laravel/framework.git",
-                "reference": "56789e9dec750e0fbe8e9e6ae90a01a4e6887902"
+                "reference": "e47180500498cf8aa2a8ffb59a3b4daa007fa13d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/laravel/framework/zipball/56789e9dec750e0fbe8e9e6ae90a01a4e6887902",
-                "reference": "56789e9dec750e0fbe8e9e6ae90a01a4e6887902",
+                "url": "https://api.github.com/repos/laravel/framework/zipball/e47180500498cf8aa2a8ffb59a3b4daa007fa13d",
+                "reference": "e47180500498cf8aa2a8ffb59a3b4daa007fa13d",
                 "shasum": ""
             },
             "require": {
                 "league/flysystem-sftp": "Required to use the Flysystem SFTP driver (^1.0).",
                 "moontoast/math": "Required to use ordered UUIDs (^1.1).",
                 "pda/pheanstalk": "Required to use the beanstalk queue driver (^4.0).",
-                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^3.0).",
+                "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0)",
+                "pusher/pusher-php-server": "Required to use the Pusher broadcast driver (^4.0).",
                 "symfony/cache": "Required to PSR-6 cache bridge (^4.3.4).",
                 "symfony/psr-http-message-bridge": "Required to use PSR-7 bridging features (^1.2).",
                 "wildbit/swiftmailer-postmark": "Required to use Postmark mail driver (^3.0)."
                 "framework",
                 "laravel"
             ],
-            "time": "2019-09-10T18:46:24+00:00"
+            "time": "2019-11-12T15:20:18+00:00"
         },
         {
             "name": "laravel/socialite",
         },
         {
             "name": "league/flysystem",
-            "version": "1.0.55",
+            "version": "1.0.57",
             "source": {
                 "type": "git",
                 "url": "https://github.com/thephpleague/flysystem.git",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6"
+                "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/33c91155537c6dc899eacdc54a13ac6303f156e6",
-                "reference": "33c91155537c6dc899eacdc54a13ac6303f156e6",
+                "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a",
+                "reference": "0e9db7f0b96b9f12dcf6f65bc34b72b1a30ea55a",
                 "shasum": ""
             },
             "require": {
                 "sftp",
                 "storage"
             ],
-            "time": "2019-08-24T11:17:19+00:00"
+            "time": "2019-10-16T21:01:05+00:00"
         },
         {
             "name": "league/flysystem-aws-s3-v3",
         },
         {
             "name": "monolog/monolog",
-            "version": "2.0.0",
+            "version": "2.0.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/monolog.git",
-                "reference": "68545165e19249013afd1d6f7485aecff07a2d22"
+                "reference": "f9d56fd2f5533322caccdfcddbb56aedd622ef1c"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/68545165e19249013afd1d6f7485aecff07a2d22",
-                "reference": "68545165e19249013afd1d6f7485aecff07a2d22",
+                "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f9d56fd2f5533322caccdfcddbb56aedd622ef1c",
+                "reference": "f9d56fd2f5533322caccdfcddbb56aedd622ef1c",
                 "shasum": ""
             },
             "require": {
                 "logging",
                 "psr-3"
             ],
-            "time": "2019-08-30T09:56:44+00:00"
+            "time": "2019-11-13T10:27:43+00:00"
         },
         {
             "name": "mtdowling/jmespath.php",
         },
         {
             "name": "nesbot/carbon",
-            "version": "2.24.0",
+            "version": "2.26.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/briannesbitt/Carbon.git",
-                "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29"
+                "reference": "e01ecc0b71168febb52ae1fdc1cfcc95428e604e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/934459c5ac0658bc765ad1e53512c7c77adcac29",
-                "reference": "934459c5ac0658bc765ad1e53512c7c77adcac29",
+                "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/e01ecc0b71168febb52ae1fdc1cfcc95428e604e",
+                "reference": "e01ecc0b71168febb52ae1fdc1cfcc95428e604e",
                 "shasum": ""
             },
             "require": {
                     "homepage": "http://github.com/kylekatarnls"
                 }
             ],
-            "description": "A API extension for DateTime that supports 281 different languages.",
+            "description": "An API extension for DateTime that supports 281 different languages.",
             "homepage": "http://carbon.nesbot.com",
             "keywords": [
                 "date",
                 "datetime",
                 "time"
             ],
-            "time": "2019-08-31T16:37:55+00:00"
+            "time": "2019-10-21T21:32:25+00:00"
+        },
+        {
+            "name": "onelogin/php-saml",
+            "version": "3.3.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/onelogin/php-saml.git",
+                "reference": "bb34489635cd5c7eb1b42833e4c57ca1c786a81a"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/onelogin/php-saml/zipball/bb34489635cd5c7eb1b42833e4c57ca1c786a81a",
+                "reference": "bb34489635cd5c7eb1b42833e4c57ca1c786a81a",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.4",
+                "robrichards/xmlseclibs": ">=3.0.4"
+            },
+            "require-dev": {
+                "pdepend/pdepend": "^2.5.0",
+                "php-coveralls/php-coveralls": "^1.0.2 || ^2.0",
+                "phploc/phploc": "^2.1 || ^3.0 || ^4.0",
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1",
+                "sebastian/phpcpd": "^2.0 || ^3.0 || ^4.0",
+                "squizlabs/php_codesniffer": "^3.1.1"
+            },
+            "suggest": {
+                "ext-curl": "Install curl lib to be able to use the IdPMetadataParser for parsing remote XMLs",
+                "ext-gettext": "Install gettext and php5-gettext libs to handle translations",
+                "ext-openssl": "Install openssl lib in order to handle with x509 certs (require to support sign and encryption)"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "OneLogin\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "description": "OneLogin PHP SAML Toolkit",
+            "homepage": "https://developers.onelogin.com/saml/php",
+            "keywords": [
+                "SAML2",
+                "onelogin",
+                "saml"
+            ],
+            "time": "2019-11-06T16:59:38+00:00"
         },
         {
             "name": "opis/closure",
-            "version": "3.4.0",
+            "version": "3.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/opis/closure.git",
-                "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7"
+                "reference": "e79f851749c3caa836d7ccc01ede5828feb762c7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/opis/closure/zipball/60a97fff133b1669a5b1776aa8ab06db3f3962b7",
-                "reference": "60a97fff133b1669a5b1776aa8ab06db3f3962b7",
+                "url": "https://api.github.com/repos/opis/closure/zipball/e79f851749c3caa836d7ccc01ede5828feb762c7",
+                "reference": "e79f851749c3caa836d7ccc01ede5828feb762c7",
                 "shasum": ""
             },
             "require": {
                 "serialization",
                 "serialize"
             ],
-            "time": "2019-09-02T21:07:33+00:00"
+            "time": "2019-10-19T18:38:51+00:00"
         },
         {
             "name": "paragonie/random_compat",
         },
         {
             "name": "phpoption/phpoption",
-            "version": "1.5.0",
+            "version": "1.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/schmittjoh/php-option.git",
-                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed"
+                "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/94e644f7d2051a5f0fcf77d81605f152eecff0ed",
-                "reference": "94e644f7d2051a5f0fcf77d81605f152eecff0ed",
+                "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/2ba2586380f8d2b44ad1b9feb61c371020b27793",
+                "reference": "2ba2586380f8d2b44ad1b9feb61c371020b27793",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "4.7.*"
+                "phpunit/phpunit": "^4.7|^5.0"
             },
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.3-dev"
+                    "dev-master": "1.5-dev"
                 }
             },
             "autoload": {
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
-                "Apache2"
+                "Apache-2.0"
             ],
             "authors": [
                 {
                 "php",
                 "type"
             ],
-            "time": "2015-07-25T16:39:46+00:00"
+            "time": "2019-11-06T22:27:00+00:00"
         },
         {
             "name": "predis/predis",
         },
         {
             "name": "psr/log",
-            "version": "1.1.0",
+            "version": "1.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/php-fig/log.git",
-                "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
+                "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
-                "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
+                "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.1.x-dev"
                 }
             },
             "autoload": {
                 "psr",
                 "psr-3"
             ],
-            "time": "2018-11-20T15:27:04+00:00"
+            "time": "2019-11-01T11:05:21+00:00"
         },
         {
             "name": "psr/simple-cache",
             ],
             "time": "2018-07-19T23:38:55+00:00"
         },
+        {
+            "name": "robrichards/xmlseclibs",
+            "version": "3.0.4",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/robrichards/xmlseclibs.git",
+                "reference": "0a53d3c3aa87564910cae4ed01416441d3ae0db5"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/0a53d3c3aa87564910cae4ed01416441d3ae0db5",
+                "reference": "0a53d3c3aa87564910cae4ed01416441d3ae0db5",
+                "shasum": ""
+            },
+            "require": {
+                "ext-openssl": "*",
+                "php": ">= 5.4"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "RobRichards\\XMLSecLibs\\": "src"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-3-Clause"
+            ],
+            "description": "A PHP library for XML Security",
+            "homepage": "https://github.com/robrichards/xmlseclibs",
+            "keywords": [
+                "security",
+                "signature",
+                "xml",
+                "xmldsig"
+            ],
+            "time": "2019-11-05T11:44:22+00:00"
+        },
         {
             "name": "sabberworm/php-css-parser",
             "version": "8.3.0",
         },
         {
             "name": "socialiteproviders/manager",
-            "version": "v3.4.2",
+            "version": "v3.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Manager.git",
-                "reference": "e3e8e78b9a3060801cd008941a0894a0a0c479e1"
+                "reference": "09903d33429f9f6c0da32c545c036a3e18964bbf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/e3e8e78b9a3060801cd008941a0894a0a0c479e1",
-                "reference": "e3e8e78b9a3060801cd008941a0894a0a0c479e1",
+                "url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/09903d33429f9f6c0da32c545c036a3e18964bbf",
+                "reference": "09903d33429f9f6c0da32c545c036a3e18964bbf",
                 "shasum": ""
             },
             "require": {
                 }
             ],
             "description": "Easily add new or override built-in providers in Laravel Socialite.",
-            "time": "2019-09-09T03:07:52+00:00"
+            "time": "2019-09-25T06:06:35+00:00"
         },
         {
             "name": "socialiteproviders/microsoft-azure",
         },
         {
             "name": "socialiteproviders/slack",
-            "version": "v3.0.3",
+            "version": "v3.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Slack.git",
-                "reference": "8d5d0c0c916adf2af6b406679130441db0afc387"
+                "reference": "d46826640fbeae8f34328d99c358404a1e1050a3"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/8d5d0c0c916adf2af6b406679130441db0afc387",
-                "reference": "8d5d0c0c916adf2af6b406679130441db0afc387",
+                "url": "https://api.github.com/repos/SocialiteProviders/Slack/zipball/d46826640fbeae8f34328d99c358404a1e1050a3",
+                "reference": "d46826640fbeae8f34328d99c358404a1e1050a3",
                 "shasum": ""
             },
             "require": {
                 }
             ],
             "description": "Slack OAuth2 Provider for Laravel Socialite",
-            "time": "2017-04-10T05:10:48+00:00"
+            "time": "2019-01-11T19:48:14+00:00"
         },
         {
             "name": "socialiteproviders/twitch",
-            "version": "v5.0.0",
+            "version": "v5.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/SocialiteProviders/Twitch.git",
-                "reference": "8c19b26ff24c40cc019413042a5492c5ed21a658"
+                "reference": "f9b1f90a94f539e1b29e84ee0f731f42d59f3213"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/8c19b26ff24c40cc019413042a5492c5ed21a658",
-                "reference": "8c19b26ff24c40cc019413042a5492c5ed21a658",
+                "url": "https://api.github.com/repos/SocialiteProviders/Twitch/zipball/f9b1f90a94f539e1b29e84ee0f731f42d59f3213",
+                "reference": "f9b1f90a94f539e1b29e84ee0f731f42d59f3213",
                 "shasum": ""
             },
             "require": {
                 }
             ],
             "description": "Twitch OAuth2 Provider for Laravel Socialite",
-            "time": "2018-06-20T10:59:51+00:00"
+            "time": "2019-07-01T10:35:46+00:00"
         },
         {
             "name": "swiftmailer/swiftmailer",
-            "version": "v6.2.1",
+            "version": "v6.2.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/swiftmailer/swiftmailer.git",
-                "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a"
+                "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
-                "reference": "5397cd05b0a0f7937c47b0adcb4c60e5ab936b6a",
+                "url": "https://api.github.com/repos/swiftmailer/swiftmailer/zipball/149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
+                "reference": "149cfdf118b169f7840bbe3ef0d4bc795d1780c9",
                 "shasum": ""
             },
             "require": {
                 "mail",
                 "mailer"
             ],
-            "time": "2019-04-21T09:21:45+00:00"
+            "time": "2019-11-12T09:31:26+00:00"
         },
         {
             "name": "symfony/console",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/console.git",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36"
+                "reference": "831424efae0a1fe6642784bd52aae14ece6538e6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/console/zipball/de63799239b3881b8a08f8481b22348f77ed7b36",
-                "reference": "de63799239b3881b8a08f8481b22348f77ed7b36",
+                "url": "https://api.github.com/repos/symfony/console/zipball/831424efae0a1fe6642784bd52aae14ece6538e6",
+                "reference": "831424efae0a1fe6642784bd52aae14ece6538e6",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Console Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-11-13T07:29:07+00:00"
         },
         {
             "name": "symfony/css-selector",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/css-selector.git",
-                "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03"
+                "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/css-selector/zipball/c6e5e2a00db768c92c3ae131532af4e1acc7bd03",
-                "reference": "c6e5e2a00db768c92c3ae131532af4e1acc7bd03",
+                "url": "https://api.github.com/repos/symfony/css-selector/zipball/f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9",
+                "reference": "f4b3ff6a549d9ed28b2b0ecd1781bf67cf220ee9",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony CssSelector Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-20T14:07:54+00:00"
+            "time": "2019-10-02T08:36:26+00:00"
         },
         {
             "name": "symfony/debug",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/debug.git",
-                "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced"
+                "reference": "5ea9c3e01989a86ceaa0283f21234b12deadf5e2"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/debug/zipball/afcdea44a2e399c1e4b52246ec8d54c715393ced",
-                "reference": "afcdea44a2e399c1e4b52246ec8d54c715393ced",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/5ea9c3e01989a86ceaa0283f21234b12deadf5e2",
+                "reference": "5ea9c3e01989a86ceaa0283f21234b12deadf5e2",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Debug Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-20T14:27:59+00:00"
+            "time": "2019-10-28T17:07:32+00:00"
         },
         {
             "name": "symfony/event-dispatcher",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher.git",
-                "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2"
+                "reference": "0df002fd4f500392eabd243c2947061a50937287"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
-                "reference": "429d0a1451d4c9c4abe1959b2986b88794b9b7d2",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0df002fd4f500392eabd243c2947061a50937287",
+                "reference": "0df002fd4f500392eabd243c2947061a50937287",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony EventDispatcher Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "time": "2019-11-03T09:04:05+00:00"
         },
         {
             "name": "symfony/event-dispatcher-contracts",
-            "version": "v1.1.5",
+            "version": "v1.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/event-dispatcher-contracts.git",
-                "reference": "c61766f4440ca687de1084a5c00b08e167a2575c"
+                "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c61766f4440ca687de1084a5c00b08e167a2575c",
-                "reference": "c61766f4440ca687de1084a5c00b08e167a2575c",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/c43ab685673fb6c8d84220c77897b1d6cdbe1d18",
+                "reference": "c43ab685673fb6c8d84220c77897b1d6cdbe1d18",
                 "shasum": ""
             },
             "require": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-06-20T06:46:26+00:00"
+            "time": "2019-09-17T09:54:03+00:00"
         },
         {
             "name": "symfony/finder",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/finder.git",
-                "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2"
+                "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/finder/zipball/86c1c929f0a4b24812e1eb109262fc3372c8e9f2",
-                "reference": "86c1c929f0a4b24812e1eb109262fc3372c8e9f2",
+                "url": "https://api.github.com/repos/symfony/finder/zipball/72a068f77e317ae77c0a0495236ad292cfb5ce6f",
+                "reference": "72a068f77e317ae77c0a0495236ad292cfb5ce6f",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Finder Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-14T12:26:46+00:00"
+            "time": "2019-10-30T12:53:54+00:00"
         },
         {
             "name": "symfony/http-foundation",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-foundation.git",
-                "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc"
+                "reference": "cabe67275034e173350e158f3b1803d023880227"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/d804bea118ff340a12e22a79f9c7e7eb56b35adc",
-                "reference": "d804bea118ff340a12e22a79f9c7e7eb56b35adc",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/cabe67275034e173350e158f3b1803d023880227",
+                "reference": "cabe67275034e173350e158f3b1803d023880227",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony HttpFoundation Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "time": "2019-11-12T13:07:20+00:00"
         },
         {
             "name": "symfony/http-kernel",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/http-kernel.git",
-                "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52"
+                "reference": "5fdf186f26f9080de531d3f1d024348b2f0ab12f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5e0fc71be03d52cd00c423061cfd300bd6f92a52",
-                "reference": "5e0fc71be03d52cd00c423061cfd300bd6f92a52",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/5fdf186f26f9080de531d3f1d024348b2f0ab12f",
+                "reference": "5fdf186f26f9080de531d3f1d024348b2f0ab12f",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony HttpKernel Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T16:47:42+00:00"
+            "time": "2019-11-13T09:07:28+00:00"
         },
         {
             "name": "symfony/mime",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/mime.git",
-                "reference": "987a05df1c6ac259b34008b932551353f4f408df"
+                "reference": "22aecf6b11638ef378fab25d6c5a2da8a31a1448"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/mime/zipball/987a05df1c6ac259b34008b932551353f4f408df",
-                "reference": "987a05df1c6ac259b34008b932551353f4f408df",
+                "url": "https://api.github.com/repos/symfony/mime/zipball/22aecf6b11638ef378fab25d6c5a2da8a31a1448",
+                "reference": "22aecf6b11638ef378fab25d6c5a2da8a31a1448",
                 "shasum": ""
             },
             "require": {
                 "mime",
                 "mime-type"
             ],
-            "time": "2019-08-22T08:16:11+00:00"
+            "time": "2019-11-12T13:10:02+00:00"
         },
         {
             "name": "symfony/polyfill-ctype",
         },
         {
             "name": "symfony/process",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/process.git",
-                "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a"
+                "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/process/zipball/e89969c00d762349f078db1128506f7f3dcc0d4a",
-                "reference": "e89969c00d762349f078db1128506f7f3dcc0d4a",
+                "url": "https://api.github.com/repos/symfony/process/zipball/3b2e0cb029afbb0395034509291f21191d1a4db0",
+                "reference": "3b2e0cb029afbb0395034509291f21191d1a4db0",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Process Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-10-28T17:07:32+00:00"
         },
         {
             "name": "symfony/routing",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/routing.git",
-                "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f"
+                "reference": "533fd12a41fb9ce8d4e861693365427849487c0e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/routing/zipball/ff1049f6232dc5b6023b1ff1c6de56f82bcd264f",
-                "reference": "ff1049f6232dc5b6023b1ff1c6de56f82bcd264f",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/533fd12a41fb9ce8d4e861693365427849487c0e",
+                "reference": "533fd12a41fb9ce8d4e861693365427849487c0e",
                 "shasum": ""
             },
             "require": {
                 "uri",
                 "url"
             ],
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-11-04T20:23:03+00:00"
         },
         {
             "name": "symfony/service-contracts",
-            "version": "v1.1.6",
+            "version": "v1.1.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/service-contracts.git",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3"
+                "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
-                "reference": "ea7263d6b6d5f798b56a45a5b8d686725f2719a3",
+                "url": "https://api.github.com/repos/symfony/service-contracts/zipball/ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
+                "reference": "ffc7f5692092df31515df2a5ecf3b7302b3ddacf",
                 "shasum": ""
             },
             "require": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-08-20T14:44:19+00:00"
+            "time": "2019-10-14T12:27:06+00:00"
         },
         {
             "name": "symfony/translation",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation.git",
-                "reference": "28498169dd334095fa981827992f3a24d50fed0f"
+                "reference": "bbce239b35b0cd47bd75848b23e969f17dd970e7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation/zipball/28498169dd334095fa981827992f3a24d50fed0f",
-                "reference": "28498169dd334095fa981827992f3a24d50fed0f",
+                "url": "https://api.github.com/repos/symfony/translation/zipball/bbce239b35b0cd47bd75848b23e969f17dd970e7",
+                "reference": "bbce239b35b0cd47bd75848b23e969f17dd970e7",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony Translation Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:55:16+00:00"
+            "time": "2019-11-06T23:21:49+00:00"
         },
         {
             "name": "symfony/translation-contracts",
-            "version": "v1.1.6",
+            "version": "v1.1.7",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/translation-contracts.git",
-                "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a"
+                "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
-                "reference": "325b17c24f3ee23cbecfa63ba809c6d89b5fa04a",
+                "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/364518c132c95642e530d9b2d217acbc2ccac3e6",
+                "reference": "364518c132c95642e530d9b2d217acbc2ccac3e6",
                 "shasum": ""
             },
             "require": {
                 "interoperability",
                 "standards"
             ],
-            "time": "2019-08-02T12:15:04+00:00"
+            "time": "2019-09-17T11:12:18+00:00"
         },
         {
             "name": "symfony/var-dumper",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/var-dumper.git",
-                "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6"
+                "reference": "ea4940845535c85ff5c505e13b3205b0076d07bf"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/641043e0f3e615990a0f29479f9c117e8a6698c6",
-                "reference": "641043e0f3e615990a0f29479f9c117e8a6698c6",
+                "url": "https://api.github.com/repos/symfony/var-dumper/zipball/ea4940845535c85ff5c505e13b3205b0076d07bf",
+                "reference": "ea4940845535c85ff5c505e13b3205b0076d07bf",
                 "shasum": ""
             },
             "require": {
                 "debug",
                 "dump"
             ],
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-10-13T12:02:04+00:00"
         },
         {
             "name": "tijsverkoyen/css-to-inline-styles",
-            "version": "2.2.1",
+            "version": "2.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/tijsverkoyen/CssToInlineStyles.git",
-                "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757"
+                "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757",
-                "reference": "0ed4a2ea4e0902dac0489e6436ebcd5bbcae9757",
+                "url": "https://api.github.com/repos/tijsverkoyen/CssToInlineStyles/zipball/dda2ee426acd6d801d5b7fd1001cde9b5f790e15",
+                "reference": "dda2ee426acd6d801d5b7fd1001cde9b5f790e15",
                 "shasum": ""
             },
             "require": {
+                "ext-dom": "*",
+                "ext-libxml": "*",
                 "php": "^5.5 || ^7.0",
-                "symfony/css-selector": "^2.7 || ^3.0 || ^4.0"
+                "symfony/css-selector": "^2.7 || ^3.0 || ^4.0 || ^5.0"
             },
             "require-dev": {
                 "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0"
             ],
             "description": "CssToInlineStyles is a class that enables you to convert HTML-pages/files into HTML-pages/files with inline styles. This is very useful when you're sending emails.",
             "homepage": "https://github.com/tijsverkoyen/CssToInlineStyles",
-            "time": "2017-11-27T11:13:29+00:00"
+            "time": "2019-10-24T08:53:34+00:00"
         },
         {
             "name": "vlucas/phpdotenv",
         },
         {
             "name": "composer/composer",
-            "version": "1.9.0",
+            "version": "1.9.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/composer.git",
-                "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5"
+                "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/composer/zipball/314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
-                "reference": "314aa57fdcfc942065996f59fb73a8b3f74f3fa5",
+                "url": "https://api.github.com/repos/composer/composer/zipball/bb01f2180df87ce7992b8331a68904f80439dd2f",
+                "reference": "bb01f2180df87ce7992b8331a68904f80439dd2f",
                 "shasum": ""
             },
             "require": {
                 "dependency",
                 "package"
             ],
-            "time": "2019-08-02T18:55:33+00:00"
+            "time": "2019-11-01T16:20:17+00:00"
         },
         {
             "name": "composer/semver",
         },
         {
             "name": "composer/xdebug-handler",
-            "version": "1.3.3",
+            "version": "1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/composer/xdebug-handler.git",
-                "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f"
+                "reference": "cbe23383749496fe0f373345208b79568e4bc248"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/46867cbf8ca9fb8d60c506895449eb799db1184f",
-                "reference": "46867cbf8ca9fb8d60c506895449eb799db1184f",
+                "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/cbe23383749496fe0f373345208b79568e4bc248",
+                "reference": "cbe23383749496fe0f373345208b79568e4bc248",
                 "shasum": ""
             },
             "require": {
-                "php": "^5.3.2 || ^7.0",
+                "php": "^5.3.2 || ^7.0 || ^8.0",
                 "psr/log": "^1.0"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5"
+                "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8"
             },
             "type": "library",
             "autoload": {
                     "email": "[email protected]"
                 }
             ],
-            "description": "Restarts a process without xdebug.",
+            "description": "Restarts a process without Xdebug.",
             "keywords": [
                 "Xdebug",
                 "performance"
             ],
-            "time": "2019-05-27T17:52:04+00:00"
+            "time": "2019-11-06T16:40:04+00:00"
         },
         {
             "name": "doctrine/instantiator",
-            "version": "1.2.0",
+            "version": "1.3.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/doctrine/instantiator.git",
-                "reference": "a2c590166b2133a4633738648b6b064edae0814a"
+                "reference": "ae466f726242e637cebdd526a7d991b9433bacf1"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/a2c590166b2133a4633738648b6b064edae0814a",
-                "reference": "a2c590166b2133a4633738648b6b064edae0814a",
+                "url": "https://api.github.com/repos/doctrine/instantiator/zipball/ae466f726242e637cebdd526a7d991b9433bacf1",
+                "reference": "ae466f726242e637cebdd526a7d991b9433bacf1",
                 "shasum": ""
             },
             "require": {
                 "constructor",
                 "instantiate"
             ],
-            "time": "2019-03-17T17:37:11+00:00"
+            "time": "2019-10-21T16:45:58+00:00"
         },
         {
             "name": "facade/flare-client-php",
-            "version": "1.0.4",
+            "version": "1.1.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/flare-client-php.git",
-                "reference": "7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e"
+                "reference": "04c0bbd1881942f59e27877bac3b29ba57519666"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e",
-                "reference": "7128b251b48f24ef64e5cddd7f8d40cc3a06fd3e",
+                "url": "https://api.github.com/repos/facade/flare-client-php/zipball/04c0bbd1881942f59e27877bac3b29ba57519666",
+                "reference": "04c0bbd1881942f59e27877bac3b29ba57519666",
                 "shasum": ""
             },
             "require": {
             },
             "require-dev": {
                 "larapack/dd": "^1.1",
-                "phpunit/phpunit": "^7.0",
+                "phpunit/phpunit": "^7.5.16",
                 "spatie/phpunit-snapshot-assertions": "^2.0"
             },
             "type": "library",
                 "flare",
                 "reporting"
             ],
-            "time": "2019-09-11T14:19:56+00:00"
+            "time": "2019-11-08T11:11:17+00:00"
         },
         {
             "name": "facade/ignition",
-            "version": "1.6.5",
+            "version": "1.12.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/facade/ignition.git",
-                "reference": "97244f6d511332f3574acab8242c09ddcfda892b"
+                "reference": "67736a01597b9e08f00a1fc8966b92b918dba5ea"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/facade/ignition/zipball/97244f6d511332f3574acab8242c09ddcfda892b",
-                "reference": "97244f6d511332f3574acab8242c09ddcfda892b",
+                "url": "https://api.github.com/repos/facade/ignition/zipball/67736a01597b9e08f00a1fc8966b92b918dba5ea",
+                "reference": "67736a01597b9e08f00a1fc8966b92b918dba5ea",
                 "shasum": ""
             },
             "require": {
                 "ext-json": "*",
                 "ext-mbstring": "*",
-                "facade/flare-client-php": "^1.0.4",
+                "facade/flare-client-php": "^1.1",
                 "facade/ignition-contracts": "^1.0",
                 "filp/whoops": "^2.4",
                 "illuminate/support": "~5.5.0 || ~5.6.0 || ~5.7.0 || ~5.8.0 || ^6.0",
             "autoload": {
                 "psr-4": {
                     "Facade\\Ignition\\": "src"
-                }
+                },
+                "files": [
+                    "src/helpers.php"
+                ]
             },
             "notification-url": "https://packagist.org/downloads/",
             "license": [
                 "laravel",
                 "page"
             ],
-            "time": "2019-09-13T13:38:04+00:00"
+            "time": "2019-11-14T10:51:35+00:00"
         },
         {
             "name": "facade/ignition-contracts",
         },
         {
             "name": "fzaninotto/faker",
-            "version": "v1.8.0",
+            "version": "v1.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/fzaninotto/Faker.git",
-                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de"
+                "reference": "27a216cbe72327b2d6369fab721a5843be71e57d"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/f72816b43e74063c8b10357394b6bba8cb1c10de",
-                "reference": "f72816b43e74063c8b10357394b6bba8cb1c10de",
+                "url": "https://api.github.com/repos/fzaninotto/Faker/zipball/27a216cbe72327b2d6369fab721a5843be71e57d",
+                "reference": "27a216cbe72327b2d6369fab721a5843be71e57d",
                 "shasum": ""
             },
             "require": {
             "require-dev": {
                 "ext-intl": "*",
                 "phpunit/phpunit": "^4.8.35 || ^5.7",
-                "squizlabs/php_codesniffer": "^1.5"
+                "squizlabs/php_codesniffer": "^2.9.2"
             },
             "type": "library",
             "extra": {
-                "branch-alias": {
-                    "dev-master": "1.8-dev"
-                }
+                "branch-alias": []
             },
             "autoload": {
                 "psr-4": {
                 "faker",
                 "fixtures"
             ],
-            "time": "2018-07-12T10:23:15+00:00"
+            "time": "2019-11-14T13:13:06+00:00"
         },
         {
             "name": "hamcrest/hamcrest-php",
         },
         {
             "name": "justinrainbow/json-schema",
-            "version": "5.2.8",
+            "version": "5.2.9",
             "source": {
                 "type": "git",
                 "url": "https://github.com/justinrainbow/json-schema.git",
-                "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4"
+                "reference": "44c6787311242a979fa15c704327c20e7221a0e4"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/dcb6e1006bb5fd1e392b4daa68932880f37550d4",
-                "reference": "dcb6e1006bb5fd1e392b4daa68932880f37550d4",
+                "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/44c6787311242a979fa15c704327c20e7221a0e4",
+                "reference": "44c6787311242a979fa15c704327c20e7221a0e4",
                 "shasum": ""
             },
             "require": {
                 "php": ">=5.3.3"
             },
             "require-dev": {
-                "friendsofphp/php-cs-fixer": "~2.2.20",
+                "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1",
                 "json-schema/json-schema-test-suite": "1.2.0",
                 "phpunit/phpunit": "^4.8.35"
             },
                 "json",
                 "schema"
             ],
-            "time": "2019-01-14T23:55:14+00:00"
+            "time": "2019-09-25T14:49:45+00:00"
         },
         {
             "name": "laravel/browser-kit-testing",
         },
         {
             "name": "maximebf/debugbar",
-            "version": "v1.15.0",
+            "version": "v1.15.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/maximebf/php-debugbar.git",
-                "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07"
+                "reference": "6c4277f6117e4864966c9cb58fb835cee8c74a1e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
-                "reference": "30e7d60937ee5f1320975ca9bc7bcdd44d500f07",
+                "url": "https://api.github.com/repos/maximebf/php-debugbar/zipball/6c4277f6117e4864966c9cb58fb835cee8c74a1e",
+                "reference": "6c4277f6117e4864966c9cb58fb835cee8c74a1e",
                 "shasum": ""
             },
             "require": {
-                "php": ">=5.3.0",
+                "php": ">=5.6",
                 "psr/log": "^1.0",
-                "symfony/var-dumper": "^2.6|^3.0|^4.0"
+                "symfony/var-dumper": "^2.6|^3|^4"
             },
             "require-dev": {
-                "phpunit/phpunit": "^4.0|^5.0"
+                "phpunit/phpunit": "^5"
             },
             "suggest": {
                 "kriswallsmith/assetic": "The best way to manage assets",
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.14-dev"
+                    "dev-master": "1.15-dev"
                 }
             },
             "autoload": {
                 "debug",
                 "debugbar"
             ],
-            "time": "2017-12-15T11:13:46+00:00"
+            "time": "2019-09-24T14:55:42+00:00"
         },
         {
             "name": "mockery/mockery",
-            "version": "1.2.3",
+            "version": "1.2.4",
             "source": {
                 "type": "git",
                 "url": "https://github.com/mockery/mockery.git",
-                "reference": "4eff936d83eb809bde2c57a3cea0ee9643769031"
+                "reference": "b3453f75fd23d9fd41685f2148f4abeacabc6405"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/mockery/mockery/zipball/4eff936d83eb809bde2c57a3cea0ee9643769031",
-                "reference": "4eff936d83eb809bde2c57a3cea0ee9643769031",
+                "url": "https://api.github.com/repos/mockery/mockery/zipball/b3453f75fd23d9fd41685f2148f4abeacabc6405",
+                "reference": "b3453f75fd23d9fd41685f2148f4abeacabc6405",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "1.0.x-dev"
+                    "dev-master": "1.2.x-dev"
                 }
             },
             "autoload": {
                 "test double",
                 "testing"
             ],
-            "time": "2019-08-07T15:01:07+00:00"
+            "time": "2019-09-30T08:30:27+00:00"
         },
         {
             "name": "myclabs/deep-copy",
         },
         {
             "name": "phpspec/prophecy",
-            "version": "1.8.1",
+            "version": "1.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/phpspec/prophecy.git",
-                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76"
+                "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
-                "reference": "1927e75f4ed19131ec9bcc3b002e07fb1173ee76",
+                "url": "https://api.github.com/repos/phpspec/prophecy/zipball/f6811d96d97bdf400077a0cc100ae56aa32b9203",
+                "reference": "f6811d96d97bdf400077a0cc100ae56aa32b9203",
                 "shasum": ""
             },
             "require": {
                 "doctrine/instantiator": "^1.0.2",
                 "php": "^5.3|^7.0",
-                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0",
+                "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0",
                 "sebastian/comparator": "^1.1|^2.0|^3.0",
                 "sebastian/recursion-context": "^1.0|^2.0|^3.0"
             },
                 "spy",
                 "stub"
             ],
-            "time": "2019-06-13T12:50:23+00:00"
+            "time": "2019-10-03T11:07:50+00:00"
         },
         {
             "name": "phpunit/php-code-coverage",
-            "version": "7.0.7",
+            "version": "7.0.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
-                "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800"
+                "reference": "aa0d179a13284c7420fc281fc32750e6cc7c9e2f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7743bbcfff2a907e9ee4a25be13d0f8ec5e73800",
-                "reference": "7743bbcfff2a907e9ee4a25be13d0f8ec5e73800",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa0d179a13284c7420fc281fc32750e6cc7c9e2f",
+                "reference": "aa0d179a13284c7420fc281fc32750e6cc7c9e2f",
                 "shasum": ""
             },
             "require": {
                 "php": "^7.2",
                 "phpunit/php-file-iterator": "^2.0.2",
                 "phpunit/php-text-template": "^1.2.1",
-                "phpunit/php-token-stream": "^3.1.0",
+                "phpunit/php-token-stream": "^3.1.1",
                 "sebastian/code-unit-reverse-lookup": "^1.0.1",
                 "sebastian/environment": "^4.2.2",
                 "sebastian/version": "^2.0.1",
                 "testing",
                 "xunit"
             ],
-            "time": "2019-07-25T05:31:54+00:00"
+            "time": "2019-09-17T06:24:36+00:00"
         },
         {
             "name": "phpunit/php-file-iterator",
         },
         {
             "name": "phpunit/php-token-stream",
-            "version": "3.1.0",
+            "version": "3.1.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/php-token-stream.git",
-                "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a"
+                "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/e899757bb3df5ff6e95089132f32cd59aac2220a",
-                "reference": "e899757bb3df5ff6e95089132f32cd59aac2220a",
+                "url": "https://api.github.com/repos/sebastianbergmann/php-token-stream/zipball/995192df77f63a59e47f025390d2d1fdf8f425ff",
+                "reference": "995192df77f63a59e47f025390d2d1fdf8f425ff",
                 "shasum": ""
             },
             "require": {
             "keywords": [
                 "tokenizer"
             ],
-            "time": "2019-07-25T05:29:42+00:00"
+            "time": "2019-09-17T06:23:10+00:00"
         },
         {
             "name": "phpunit/phpunit",
-            "version": "8.3.5",
+            "version": "8.4.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "302faed7059fde575cf3403a78c730c5e3a62750"
+                "reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/302faed7059fde575cf3403a78c730c5e3a62750",
-                "reference": "302faed7059fde575cf3403a78c730c5e3a62750",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/67f9e35bffc0dd52d55d565ddbe4230454fd6a4e",
+                "reference": "67f9e35bffc0dd52d55d565ddbe4230454fd6a4e",
                 "shasum": ""
             },
             "require": {
             "type": "library",
             "extra": {
                 "branch-alias": {
-                    "dev-master": "8.3-dev"
+                    "dev-master": "8.4-dev"
                 }
             },
             "autoload": {
                 "testing",
                 "xunit"
             ],
-            "time": "2019-09-14T09:12:03+00:00"
+            "time": "2019-11-06T09:42:23+00:00"
         },
         {
             "name": "scrivo/highlight.php",
         },
         {
             "name": "seld/jsonlint",
-            "version": "1.7.1",
+            "version": "1.7.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/Seldaek/jsonlint.git",
-                "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38"
+                "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/d15f59a67ff805a44c50ea0516d2341740f81a38",
-                "reference": "d15f59a67ff805a44c50ea0516d2341740f81a38",
+                "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e2e5d290e4d2a4f0eb449f510071392e00e10d19",
+                "reference": "e2e5d290e4d2a4f0eb449f510071392e00e10d19",
                 "shasum": ""
             },
             "require": {
                 "parser",
                 "validator"
             ],
-            "time": "2018-01-24T12:46:19+00:00"
+            "time": "2019-10-24T14:27:39+00:00"
         },
         {
             "name": "seld/phar-utils",
         },
         {
             "name": "squizlabs/php_codesniffer",
-            "version": "3.4.2",
+            "version": "3.5.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/squizlabs/PHP_CodeSniffer.git",
-                "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8"
+                "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
-                "reference": "b8a7362af1cc1aadb5bd36c3defc4dda2cf5f0a8",
+                "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/65b12cdeaaa6cd276d4c3033a95b9b88b12701e7",
+                "reference": "65b12cdeaaa6cd276d4c3033a95b9b88b12701e7",
                 "shasum": ""
             },
             "require": {
                 "phpcs",
                 "standards"
             ],
-            "time": "2019-04-10T23:49:02+00:00"
+            "time": "2019-10-28T04:36:32+00:00"
         },
         {
             "name": "symfony/dom-crawler",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/dom-crawler.git",
-                "reference": "cc686552948d627528c0e2e759186dff67c2610e"
+                "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/cc686552948d627528c0e2e759186dff67c2610e",
-                "reference": "cc686552948d627528c0e2e759186dff67c2610e",
+                "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/4b9efd5708c3a38593e19b6a33e40867f4f89d72",
+                "reference": "4b9efd5708c3a38593e19b6a33e40867f4f89d72",
                 "shasum": ""
             },
             "require": {
             ],
             "description": "Symfony DomCrawler Component",
             "homepage": "https://symfony.com",
-            "time": "2019-08-26T08:26:39+00:00"
+            "time": "2019-10-28T17:07:32+00:00"
         },
         {
             "name": "symfony/filesystem",
-            "version": "v4.3.4",
+            "version": "v4.3.8",
             "source": {
                 "type": "git",
                 "url": "https://github.com/symfony/filesystem.git",
index 47afc27a15d7109a2bdec012a5735641f71ef44a..7142eebd2017ae05e97337083371e779f4c4b20f 100644 (file)
       "dev": true
     },
     "acorn": {
-      "version": "6.1.1",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz",
-      "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==",
-      "dev": true
-    },
-    "acorn-dynamic-import": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-4.0.0.tgz",
-      "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
+      "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
       "dev": true
     },
     "ajv": {
-      "version": "6.10.0",
-      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz",
-      "integrity": "sha512-nffhOpkymDECQyR0mnsUtoCE8RlX38G0rYP+wgLWFyZuUyuuojSSvi/+euOiQBIn63whYwYVIIH1TvE3tu4OEg==",
+      "version": "6.10.2",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
+      "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
       "dev": true,
       "requires": {
         "fast-deep-equal": "^2.0.1",
       }
     },
     "ajv-errors": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.0.tgz",
-      "integrity": "sha1-7PAh+hCP0X37Xms4Py3SM+Mf/Fk=",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
+      "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
       "dev": true
     },
     "ajv-keywords": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.1.0.tgz",
-      "integrity": "sha1-rCsnk5xUPpXSwG5/f1wnvkqlQ74=",
+      "version": "3.4.1",
+      "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
+      "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
       "dev": true
     },
     "amdefine": {
       "dev": true
     },
     "async-each": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz",
-      "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=",
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz",
+      "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==",
       "dev": true
     },
     "async-foreach": {
       "integrity": "sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=",
       "dev": true
     },
+    "async-limiter": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz",
+      "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==",
+      "dev": true
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
       "dev": true
     },
     "aws4": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz",
-      "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==",
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
+      "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==",
       "dev": true
     },
     "balanced-match": {
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
     "base64-js": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz",
-      "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==",
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
+      "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
       "dev": true
     },
     "bcrypt-pbkdf": {
       }
     },
     "big.js": {
-      "version": "3.2.0",
-      "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz",
-      "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==",
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
+      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
       "dev": true
     },
     "binary-extensions": {
-      "version": "1.10.0",
-      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz",
-      "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=",
+      "version": "1.13.1",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
       "dev": true
     },
+    "bindings": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
+      "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "file-uri-to-path": "1.0.0"
+      }
+    },
     "block-stream": {
       "version": "0.0.9",
       "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz",
       }
     },
     "bluebird": {
-      "version": "3.5.5",
-      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.5.tgz",
-      "integrity": "sha512-5am6HnnfN+urzt4yfg7IgTbotDjIT/u8AJpEt0sIU9FtXfVeezXAPKswrG+xKUCOYAINpSdgZVDU6QFh+cuH3w==",
+      "version": "3.7.2",
+      "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
       "dev": true
     },
     "bn.js": {
         "des.js": "^1.0.0",
         "inherits": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "browserify-rsa": {
       }
     },
     "buffer": {
-      "version": "4.9.1",
-      "resolved": "http://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz",
-      "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=",
+      "version": "4.9.2",
+      "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+      "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
       "dev": true,
       "requires": {
         "base64-js": "^1.0.2",
       "dev": true
     },
     "cacache": {
-      "version": "11.3.2",
-      "resolved": "https://registry.npmjs.org/cacache/-/cacache-11.3.2.tgz",
-      "integrity": "sha512-E0zP4EPGDOaT2chM08Als91eYnf8Z+eH1awwwVsngUmgppfM5jjJ8l3z5vO5p5w/I3LsiXawb1sW0VY65pQABg==",
+      "version": "12.0.3",
+      "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.3.tgz",
+      "integrity": "sha512-kqdmfXEGFepesTuROHMs3MpFLWrPkSSpRqOw80RCflZXy/khxaArvFrQ7uJxSUduzAufc6G0g1VUCOZXxWavPw==",
       "dev": true,
       "requires": {
-        "bluebird": "^3.5.3",
+        "bluebird": "^3.5.5",
         "chownr": "^1.1.1",
         "figgy-pudding": "^3.5.1",
-        "glob": "^7.1.3",
+        "glob": "^7.1.4",
         "graceful-fs": "^4.1.15",
+        "infer-owner": "^1.0.3",
         "lru-cache": "^5.1.1",
         "mississippi": "^3.0.0",
         "mkdirp": "^0.5.1",
         "move-concurrently": "^1.0.1",
         "promise-inflight": "^1.0.1",
-        "rimraf": "^2.6.2",
+        "rimraf": "^2.6.3",
         "ssri": "^6.0.1",
         "unique-filename": "^1.1.1",
         "y18n": "^4.0.0"
       },
       "dependencies": {
         "graceful-fs": {
-          "version": "4.1.15",
-          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
-          "integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
+          "version": "4.2.3",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+          "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
           "dev": true
         },
         "lru-cache": {
           "dev": true
         },
         "yallist": {
-          "version": "3.0.3",
-          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz",
-          "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==",
+          "version": "3.1.1",
+          "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+          "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
           "dev": true
         }
       }
       }
     },
     "chokidar": {
-      "version": "2.1.6",
-      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.6.tgz",
-      "integrity": "sha512-V2jUo67OKkc6ySiRpJrjlpJKl9kDuG+Xb8VgsGzb+aEouhgS1D0weyPU4lEzdAcsCAvrih2J2BqyXqHWvVLw5g==",
+      "version": "2.1.8",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+      "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
       "dev": true,
       "requires": {
         "anymatch": "^2.0.0",
       }
     },
     "chownr": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz",
-      "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.3.tgz",
+      "integrity": "sha512-i70fVHhmV3DtTl6nqvZOnIjbY0Pe4kAUjwHj8z0zAdgBtYrJyYwLKCCuRBQ5ppkyL0AkN7HKRnETdmdp1zqNXw==",
       "dev": true
     },
     "chrome-trace-event": {
       }
     },
     "clone-deep": {
-      "version": "2.0.2",
-      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-2.0.2.tgz",
-      "integrity": "sha512-SZegPTKjCgpQH63E+eN6mVEEPdQBOUzjyJm5Pora4lrwWRFS8I0QAxV/KD6vV/i0WuijHZWQC1fMsPEdxfdVCQ==",
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+      "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
       "dev": true,
       "requires": {
-        "for-own": "^1.0.0",
         "is-plain-object": "^2.0.4",
-        "kind-of": "^6.0.0",
-        "shallow-clone": "^1.0.0"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
+        "kind-of": "^6.0.2",
+        "shallow-clone": "^3.0.0"
       }
     },
     "code-point-at": {
       "dev": true
     },
     "codemirror": {
-      "version": "5.47.0",
-      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz",
-      "integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA=="
+      "version": "5.50.0",
+      "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.50.0.tgz",
+      "integrity": "sha512-32LAmGcBNhKtJP4WGgkcaCVQDyChAyaWA6jasg778ziZzo3PWBuhpAQIJMO8//Id45RoaLyXjuhcRUBoS8Vg+Q=="
     },
     "collection-visit": {
       "version": "1.0.0",
       }
     },
     "commander": {
-      "version": "2.20.0",
-      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.0.tgz",
-      "integrity": "sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==",
+      "version": "2.20.3",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
       "dev": true
     },
     "commondir": {
       }
     },
     "console-browserify": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
-      "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
-      "dev": true,
-      "requires": {
-        "date-now": "^0.1.4"
-      }
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz",
+      "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==",
+      "dev": true
     },
     "console-control-strings": {
       "version": "1.1.0",
       }
     },
     "css-loader": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
-      "integrity": "sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w==",
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.4.0.tgz",
+      "integrity": "sha512-JornYo4RAXl1Mzt0lOSVPmArzAMV3rGY2VuwtaDc732WTWjdwTaeS19nCGWMcSCf305Q396lhhDAJEWWM0SgPQ==",
       "dev": true,
       "requires": {
-        "camelcase": "^5.2.0",
-        "icss-utils": "^4.1.0",
+        "camelcase": "^5.3.1",
+        "cssesc": "^3.0.0",
+        "icss-utils": "^4.1.1",
         "loader-utils": "^1.2.3",
         "normalize-path": "^3.0.0",
-        "postcss": "^7.0.14",
+        "postcss": "^7.0.23",
         "postcss-modules-extract-imports": "^2.0.0",
-        "postcss-modules-local-by-default": "^2.0.6",
-        "postcss-modules-scope": "^2.1.0",
-        "postcss-modules-values": "^2.0.0",
-        "postcss-value-parser": "^3.3.0",
-        "schema-utils": "^1.0.0"
-      },
-      "dependencies": {
-        "big.js": {
-          "version": "5.2.2",
-          "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-          "dev": true
-        },
-        "json5": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
-        },
-        "loader-utils": {
-          "version": "1.2.3",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
-          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
-          "dev": true,
-          "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^2.0.0",
-            "json5": "^1.0.1"
-          }
-        },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
+        "postcss-modules-local-by-default": "^3.0.2",
+        "postcss-modules-scope": "^2.1.1",
+        "postcss-modules-values": "^3.0.0",
+        "postcss-value-parser": "^4.0.2",
+        "schema-utils": "^2.6.0"
       }
     },
     "cssesc": {
       }
     },
     "cyclist": {
-      "version": "0.2.2",
-      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-0.2.2.tgz",
-      "integrity": "sha1-GzN5LhHpFKL9bW7WRHRkRE5fpkA=",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
+      "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=",
       "dev": true
     },
     "dashdash": {
         "assert-plus": "^1.0.0"
       }
     },
-    "date-now": {
-      "version": "0.1.4",
-      "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
-      "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=",
-      "dev": true
-    },
     "debug": {
       "version": "2.6.9",
       "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
       "dev": true
     },
     "des.js": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.0.tgz",
-      "integrity": "sha1-wHTS4qpqipoH29YfmhXCzYPsjsw=",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz",
+      "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==",
       "dev": true,
       "requires": {
         "inherits": "^2.0.1",
       }
     },
     "elliptic": {
-      "version": "6.4.1",
-      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz",
-      "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==",
+      "version": "6.5.2",
+      "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.2.tgz",
+      "integrity": "sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw==",
       "dev": true,
       "requires": {
         "bn.js": "^4.4.0",
         "minimalistic-crypto-utils": "^1.0.0"
       }
     },
+    "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
+    },
     "emojis-list": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
       "dev": true
     },
     "end-of-stream": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz",
-      "integrity": "sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q==",
+      "version": "1.4.4",
+      "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz",
+      "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==",
       "dev": true,
       "requires": {
         "once": "^1.4.0"
       }
     },
     "enhanced-resolve": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
-      "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz",
+      "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==",
       "dev": true,
       "requires": {
         "graceful-fs": "^4.1.2",
-        "memory-fs": "^0.4.0",
+        "memory-fs": "^0.5.0",
         "tapable": "^1.0.0"
+      },
+      "dependencies": {
+        "memory-fs": {
+          "version": "0.5.0",
+          "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz",
+          "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==",
+          "dev": true,
+          "requires": {
+            "errno": "^0.1.3",
+            "readable-stream": "^2.0.1"
+          }
+        }
       }
     },
     "entities": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
-      "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
+      "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
     },
     "errno": {
       "version": "0.1.7",
       }
     },
     "estraverse": {
-      "version": "4.2.0",
-      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz",
-      "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=",
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
     "events": {
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
       "dev": true
     },
     "fast-json-stable-stringify": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
-      "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
       "dev": true
     },
     "figgy-pudding": {
       "integrity": "sha512-vNKxJHTEKNThjfrdJwHc7brvM6eVevuO5nTj6ez8ZQ1qbXTvGthucRF7S4vf2cr71QVnT70V34v0S1DyQsti0w==",
       "dev": true
     },
+    "file-uri-to-path": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
+      "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
+      "dev": true,
+      "optional": true
+    },
     "fill-range": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
       }
     },
     "find-up": {
-      "version": "3.0.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-      "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+      "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
       "dev": true,
       "requires": {
-        "locate-path": "^3.0.0"
+        "path-exists": "^2.0.0",
+        "pinkie-promise": "^2.0.0"
       }
     },
     "findup-sync": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz",
-      "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz",
+      "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==",
       "dev": true,
       "requires": {
         "detect-file": "^1.0.0",
-        "is-glob": "^3.1.0",
+        "is-glob": "^4.0.0",
         "micromatch": "^3.0.4",
         "resolve-dir": "^1.0.1"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "is-glob": {
-          "version": "3.1.0",
-          "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
-          "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
-          "dev": true,
-          "requires": {
-            "is-extglob": "^2.1.0"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        }
       }
     },
     "flush-write-stream": {
       "requires": {
         "inherits": "^2.0.3",
         "readable-stream": "^2.3.6"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
       }
     },
     "for-in": {
       "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
       "dev": true
     },
-    "for-own": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz",
-      "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs=",
-      "dev": true,
-      "requires": {
-        "for-in": "^1.0.1"
-      }
-    },
     "forever-agent": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
       "dev": true
     },
     "fsevents": {
-      "version": "1.2.9",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
-      "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+      "version": "1.2.11",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.11.tgz",
+      "integrity": "sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw==",
       "dev": true,
       "optional": true,
       "requires": {
+        "bindings": "^1.5.0",
         "nan": "^2.12.1",
-        "node-pre-gyp": "^0.12.0"
+        "node-pre-gyp": "*"
       },
       "dependencies": {
         "abbrev": {
           }
         },
         "chownr": {
-          "version": "1.1.1",
+          "version": "1.1.3",
           "bundled": true,
           "dev": true,
           "optional": true
           "optional": true
         },
         "debug": {
-          "version": "4.1.1",
+          "version": "3.2.6",
           "bundled": true,
           "dev": true,
           "optional": true,
           "optional": true
         },
         "fs-minipass": {
-          "version": "1.2.5",
+          "version": "1.2.7",
           "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "minipass": "^2.2.1"
+            "minipass": "^2.6.0"
           }
         },
         "fs.realpath": {
           }
         },
         "glob": {
-          "version": "7.1.3",
+          "version": "7.1.6",
           "bundled": true,
           "dev": true,
           "optional": true,
           }
         },
         "ignore-walk": {
-          "version": "3.0.1",
+          "version": "3.0.3",
           "bundled": true,
           "dev": true,
           "optional": true,
           }
         },
         "inherits": {
-          "version": "2.0.3",
+          "version": "2.0.4",
           "bundled": true,
           "dev": true,
           "optional": true
           "optional": true
         },
         "minipass": {
-          "version": "2.3.5",
+          "version": "2.9.0",
           "bundled": true,
           "dev": true,
           "optional": true,
           }
         },
         "minizlib": {
-          "version": "1.2.1",
+          "version": "1.3.3",
           "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "minipass": "^2.2.1"
+            "minipass": "^2.9.0"
           }
         },
         "mkdirp": {
           }
         },
         "ms": {
-          "version": "2.1.1",
+          "version": "2.1.2",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "needle": {
-          "version": "2.3.0",
+          "version": "2.4.0",
           "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
-            "debug": "^4.1.0",
+            "debug": "^3.2.6",
             "iconv-lite": "^0.4.4",
             "sax": "^1.2.4"
           }
         },
         "node-pre-gyp": {
-          "version": "0.12.0",
+          "version": "0.14.0",
           "bundled": true,
           "dev": true,
           "optional": true,
             "rc": "^1.2.7",
             "rimraf": "^2.6.1",
             "semver": "^5.3.0",
-            "tar": "^4"
+            "tar": "^4.4.2"
           }
         },
         "nopt": {
           }
         },
         "npm-bundled": {
-          "version": "1.0.6",
+          "version": "1.1.1",
+          "bundled": true,
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "npm-normalize-package-bin": "^1.0.1"
+          }
+        },
+        "npm-normalize-package-bin": {
+          "version": "1.0.1",
           "bundled": true,
           "dev": true,
           "optional": true
         },
         "npm-packlist": {
-          "version": "1.4.1",
+          "version": "1.4.7",
           "bundled": true,
           "dev": true,
           "optional": true,
           "optional": true
         },
         "process-nextick-args": {
-          "version": "2.0.0",
+          "version": "2.0.1",
           "bundled": true,
           "dev": true,
           "optional": true
           }
         },
         "rimraf": {
-          "version": "2.6.3",
+          "version": "2.7.1",
           "bundled": true,
           "dev": true,
           "optional": true,
           "optional": true
         },
         "semver": {
-          "version": "5.7.0",
+          "version": "5.7.1",
           "bundled": true,
           "dev": true,
           "optional": true
           "optional": true
         },
         "tar": {
-          "version": "4.4.8",
+          "version": "4.4.13",
           "bundled": true,
           "dev": true,
           "optional": true,
           "requires": {
             "chownr": "^1.1.1",
             "fs-minipass": "^1.2.5",
-            "minipass": "^2.3.4",
-            "minizlib": "^1.1.1",
+            "minipass": "^2.8.6",
+            "minizlib": "^1.2.1",
             "mkdirp": "^0.5.0",
             "safe-buffer": "^5.1.2",
-            "yallist": "^3.0.2"
+            "yallist": "^3.0.3"
           }
         },
         "util-deprecate": {
           "optional": true
         },
         "yallist": {
-          "version": "3.0.3",
+          "version": "3.1.1",
           "bundled": true,
           "dev": true,
           "optional": true
       }
     },
     "glob": {
-      "version": "7.1.3",
-      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
-      "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+      "version": "7.1.6",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
+      "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
       "dev": true,
       "requires": {
         "fs.realpath": "^1.0.0",
       }
     },
     "global-modules": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
-      "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz",
+      "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
       "dev": true,
       "requires": {
-        "global-prefix": "^1.0.1",
-        "is-windows": "^1.0.1",
-        "resolve-dir": "^1.0.0"
+        "global-prefix": "^3.0.0"
+      },
+      "dependencies": {
+        "global-prefix": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz",
+          "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
+          "dev": true,
+          "requires": {
+            "ini": "^1.3.5",
+            "kind-of": "^6.0.2",
+            "which": "^1.3.1"
+          }
+        }
       }
     },
     "global-prefix": {
       }
     },
     "globule": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/globule/-/globule-1.2.1.tgz",
-      "integrity": "sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==",
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/globule/-/globule-1.3.0.tgz",
+      "integrity": "sha512-YlD4kdMqRCQHrhVdonet4TdRtv1/sZKepvoxNT4Nrhrp5HI8XFfc8kFlGlBn2myBo80aGp8Eft259mbcUJhgSg==",
       "dev": true,
       "requires": {
         "glob": "~7.1.1",
       "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=",
       "dev": true
     },
-    "icss-replace-symbols": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz",
-      "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=",
-      "dev": true
-    },
     "icss-utils": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-4.1.1.tgz",
       "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=",
       "dev": true
     },
-    "indexof": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz",
-      "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=",
+    "infer-owner": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz",
+      "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==",
       "dev": true
     },
     "inflight": {
       }
     },
     "inherits": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
-      "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
       "dev": true
     },
     "ini": {
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "is-arrayish": {
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "is-date-object": {
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "is-plain-obj": {
       "dev": true
     },
     "json5": {
-      "version": "0.5.1",
-      "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
-      "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
-      "dev": true
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
+      "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
+      "dev": true,
+      "requires": {
+        "minimist": "^1.2.0"
+      }
     },
     "jsonify": {
       "version": "0.0.0",
       }
     },
     "kind-of": {
-      "version": "3.2.2",
-      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
-      "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
-      "dev": true,
-      "requires": {
-        "is-buffer": "^1.1.5"
-      }
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
+      "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
+      "dev": true
     },
     "lcid": {
       "version": "1.0.0",
       }
     },
     "linkify-it": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.0.3.tgz",
-      "integrity": "sha1-2UpGSPmxwXnWT6lykSaL22zpQ08=",
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz",
+      "integrity": "sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw==",
       "requires": {
         "uc.micro": "^1.0.1"
       }
     },
     "livereload": {
-      "version": "0.8.0",
-      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.8.0.tgz",
-      "integrity": "sha512-Hi5Na6VIK3e8zlgOS50fu+iOTKWj5hM0BE7NKpZkwnfWTnktTjA38ZUXa2NlJww8/GrdVhpnxdqlLad5fkO27g==",
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/livereload/-/livereload-0.8.2.tgz",
+      "integrity": "sha512-8wCvhiCL4cGVoT3U5xoe+UjpiiVZLrlOvr6dbhb1VlyC5QarhrlyRRt4z7EMGO4KSgXj+tKF/dr284F28/wI+g==",
       "dev": true,
       "requires": {
         "chokidar": "^2.1.5",
         "opts": ">= 1.2.0",
-        "ws": "^1.1.5"
+        "ws": "^6.2.1"
       }
     },
     "load-json-file": {
       "dev": true
     },
     "loader-utils": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz",
-      "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=",
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
+      "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
       "dev": true,
       "requires": {
-        "big.js": "^3.1.3",
+        "big.js": "^5.2.2",
         "emojis-list": "^2.0.0",
-        "json5": "^0.5.0"
+        "json5": "^1.0.1"
       }
     },
     "locate-path": {
       "requires": {
         "p-locate": "^3.0.0",
         "path-exists": "^3.0.0"
+      },
+      "dependencies": {
+        "path-exists": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+          "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
+          "dev": true
+        }
       }
     },
     "lodash": {
       "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
       "dev": true
     },
-    "lodash.tail": {
-      "version": "4.1.1",
-      "resolved": "https://registry.npmjs.org/lodash.tail/-/lodash.tail-4.1.1.tgz",
-      "integrity": "sha1-0jM6NtnncXyK0vfKyv7HwytERmQ=",
-      "dev": true
-    },
     "loud-rejection": {
       "version": "1.6.0",
       "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
       }
     },
     "markdown-it": {
-      "version": "8.4.2",
-      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz",
-      "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==",
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz",
+      "integrity": "sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg==",
       "requires": {
         "argparse": "^1.0.7",
-        "entities": "~1.1.1",
+        "entities": "~2.0.0",
         "linkify-it": "^2.0.0",
         "mdurl": "^1.0.1",
         "uc.micro": "^1.0.5"
         "hash-base": "^3.0.0",
         "inherits": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "mdurl": {
         "read-pkg-up": "^1.0.1",
         "redent": "^1.0.0",
         "trim-newlines": "^1.0.0"
-      },
-      "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
-        }
       }
     },
     "micromatch": {
         "regex-not": "^1.0.0",
         "snapdragon": "^0.8.1",
         "to-regex": "^3.0.2"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "miller-rabin": {
       }
     },
     "mime-db": {
-      "version": "1.40.0",
-      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
-      "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
+      "version": "1.42.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.42.0.tgz",
+      "integrity": "sha512-UbfJCR4UAVRNgMpfImz05smAXK7+c+ZntjaA26ANtkXLlOe947Aag5zdIcKQULAiF9Cq4WxBi9jUs5zkA84bYQ==",
       "dev": true
     },
     "mime-types": {
-      "version": "2.1.24",
-      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
-      "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
+      "version": "2.1.25",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.25.tgz",
+      "integrity": "sha512-5KhStqB5xpTAeGqKBAMgwaYMnQik7teQN4IAzC7npDv6kzeU6prfkR67bc87J1kWMPGkoaZSq1npmexMgkmEVg==",
       "dev": true,
       "requires": {
-        "mime-db": "1.40.0"
+        "mime-db": "1.42.0"
       }
     },
     "mimic-fn": {
       "dev": true
     },
     "mini-css-extract-plugin": {
-      "version": "0.7.0",
-      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.7.0.tgz",
-      "integrity": "sha512-RQIw6+7utTYn8DBGsf/LpRgZCJMpZt+kuawJ/fju0KiOL6nAaTBNmCJwS7HtwSCXfS47gCkmtBFS7HdsquhdxQ==",
+      "version": "0.9.0",
+      "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-0.9.0.tgz",
+      "integrity": "sha512-lp3GeY7ygcgAmVIcRPBVhIkf8Us7FZjA+ILpal44qLdSu11wmjKQ3d9k15lfD7pO4esu9eUIAW7qiYIBppv40A==",
       "dev": true,
       "requires": {
         "loader-utils": "^1.1.0",
         "normalize-url": "1.9.1",
         "schema-utils": "^1.0.0",
         "webpack-sources": "^1.1.0"
+      },
+      "dependencies": {
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
+          "requires": {
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
+          }
+        }
       }
     },
     "minimalistic-assert": {
       }
     },
     "minimist": {
-      "version": "0.0.8",
-      "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
-      "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
       "dev": true
     },
     "mississippi": {
         }
       }
     },
-    "mixin-object": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz",
-      "integrity": "sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4=",
-      "dev": true,
-      "requires": {
-        "for-in": "^0.1.3",
-        "is-extendable": "^0.1.1"
-      },
-      "dependencies": {
-        "for-in": {
-          "version": "0.1.8",
-          "resolved": "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz",
-          "integrity": "sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE=",
-          "dev": true
-        }
-      }
-    },
     "mkdirp": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
       "dev": true,
       "requires": {
         "minimist": "0.0.8"
+      },
+      "dependencies": {
+        "minimist": {
+          "version": "0.0.8",
+          "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+          "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
+          "dev": true
+        }
       }
     },
     "move-concurrently": {
         "regex-not": "^1.0.0",
         "snapdragon": "^0.8.1",
         "to-regex": "^3.0.1"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
-        }
       }
     },
     "neo-async": {
-      "version": "2.6.0",
-      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
-      "integrity": "sha512-MFh0d/Wa7vkKO3Y3LlacqAEeHK0mckVqzDieUKTT+KGxi+zIpeVsFxymkIiRpbpDziHc290Xr9A1O4Om7otoRA==",
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz",
+      "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==",
       "dev": true
     },
     "nice-try": {
       }
     },
     "node-libs-browser": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.0.tgz",
-      "integrity": "sha512-5MQunG/oyOaBdttrL40dA7bUfPORLRWMUJLQtMg7nluxUvk5XwnLdL9twQHFAjRx/y7mIMkLKT9++qPbbk6BZA==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz",
+      "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==",
       "dev": true,
       "requires": {
         "assert": "^1.1.1",
         "events": "^3.0.0",
         "https-browserify": "^1.0.0",
         "os-browserify": "^0.3.0",
-        "path-browserify": "0.0.0",
+        "path-browserify": "0.0.1",
         "process": "^0.11.10",
         "punycode": "^1.2.4",
         "querystring-es3": "^0.2.0",
         "tty-browserify": "0.0.0",
         "url": "^0.11.0",
         "util": "^0.11.0",
-        "vm-browserify": "0.0.4"
+        "vm-browserify": "^1.0.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
       }
     },
     "node-sass": {
-      "version": "4.12.0",
-      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.12.0.tgz",
-      "integrity": "sha512-A1Iv4oN+Iel6EPv77/HddXErL2a+gZ4uBeZUy+a8O35CFYTXhgA8MgLCWBtwpGZdCvTvQ9d+bQxX/QC36GDPpQ==",
+      "version": "4.13.0",
+      "resolved": "https://registry.npmjs.org/node-sass/-/node-sass-4.13.0.tgz",
+      "integrity": "sha512-W1XBrvoJ1dy7VsvTAS5q1V45lREbTlZQqFbiHb3R3OTTCma0XBtuG6xZ6Z4506nR4lmHPTqVRwxT6KgtWC97CA==",
       "dev": true,
       "requires": {
         "async-foreach": "^0.1.3",
         "get-stdin": "^4.0.1",
         "glob": "^7.0.3",
         "in-publish": "^2.0.0",
-        "lodash": "^4.17.11",
+        "lodash": "^4.17.15",
         "meow": "^3.7.0",
         "mkdirp": "^0.5.1",
         "nan": "^2.13.2",
           "requires": {
             "is-descriptor": "^0.1.0"
           }
+        },
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
         }
       }
     },
         "wrappy": "1"
       }
     },
-    "options": {
-      "version": "0.0.6",
-      "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz",
-      "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=",
-      "dev": true
-    },
     "opts": {
-      "version": "1.2.6",
-      "resolved": "https://registry.npmjs.org/opts/-/opts-1.2.6.tgz",
-      "integrity": "sha1-0YXAQlz9652h0YKQi2W1wCOP67M=",
+      "version": "1.2.7",
+      "resolved": "https://registry.npmjs.org/opts/-/opts-1.2.7.tgz",
+      "integrity": "sha512-hwZhzGGG/GQ7igxAVFOEun2N4fWul31qE9nfBdCnZGQCB5+L7tN9xZ+94B4aUpLOJx/of3zZs5XsuubayQYQjA==",
       "dev": true
     },
     "os-browserify": {
       "dev": true
     },
     "p-limit": {
-      "version": "2.2.0",
-      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
-      "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz",
+      "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==",
       "dev": true,
       "requires": {
         "p-try": "^2.0.0"
       "dev": true
     },
     "parallel-transform": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.1.0.tgz",
-      "integrity": "sha1-1BDwZbBdojCB/NEPKIVMKb2jOwY=",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz",
+      "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==",
       "dev": true,
       "requires": {
-        "cyclist": "~0.2.2",
+        "cyclist": "^1.0.1",
         "inherits": "^2.0.3",
         "readable-stream": "^2.1.5"
       }
     },
     "parse-asn1": {
-      "version": "5.1.4",
-      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.4.tgz",
-      "integrity": "sha512-Qs5duJcuvNExRfFZ99HDD3z4mAi3r9Wl/FOjEOijlxwCZs7E7mW2vjTpgQ4J8LpTF8x5v+1Vn5UQFejmWT11aw==",
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz",
+      "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==",
       "dev": true,
       "requires": {
         "asn1.js": "^4.0.0",
       "dev": true
     },
     "path-browserify": {
-      "version": "0.0.0",
-      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz",
-      "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=",
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
+      "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==",
       "dev": true
     },
     "path-dirname": {
       "dev": true
     },
     "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
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+      "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+      "dev": true,
+      "requires": {
+        "pinkie-promise": "^2.0.0"
+      }
     },
     "path-is-absolute": {
       "version": "1.0.1",
       "dev": true,
       "requires": {
         "find-up": "^3.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        }
       }
     },
     "posix-character-classes": {
       "dev": true
     },
     "postcss": {
-      "version": "7.0.16",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.16.tgz",
-      "integrity": "sha512-MOo8zNSlIqh22Uaa3drkdIAgUGEL+AD1ESiSdmElLUmE2uVDo1QloiT/IfW9qRw8Gw+Y/w69UVMGwbufMSftxA==",
+      "version": "7.0.25",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.25.tgz",
+      "integrity": "sha512-NXXVvWq9icrm/TgQC0O6YVFi4StfJz46M1iNd/h6B26Nvh/HKI+q4YZtFN/EjcInZliEscO/WL10BXnc1E5nwg==",
       "dev": true,
       "requires": {
         "chalk": "^2.4.2",
       }
     },
     "postcss-modules-local-by-default": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz",
-      "integrity": "sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA==",
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.2.tgz",
+      "integrity": "sha512-jM/V8eqM4oJ/22j0gx4jrp63GSvDH6v86OqyTHHUvk4/k1vceipZsaymiZ5PvocqZOl5SFHiFJqjs3la0wnfIQ==",
       "dev": true,
       "requires": {
-        "postcss": "^7.0.6",
-        "postcss-selector-parser": "^6.0.0",
-        "postcss-value-parser": "^3.3.1"
+        "icss-utils": "^4.1.1",
+        "postcss": "^7.0.16",
+        "postcss-selector-parser": "^6.0.2",
+        "postcss-value-parser": "^4.0.0"
       }
     },
     "postcss-modules-scope": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.0.tgz",
-      "integrity": "sha512-91Rjps0JnmtUB0cujlc8KIKCsJXWjzuxGeT/+Q2i2HXKZ7nBUeF9YQTZZTNvHVoNYj1AthsjnGLtqDUE0Op79A==",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-2.1.1.tgz",
+      "integrity": "sha512-OXRUPecnHCg8b9xWvldG/jUpRIGPNRka0r4D4j0ESUU2/5IOnpsjfPPmDprM3Ih8CgZ8FXjWqaniK5v4rWt3oQ==",
       "dev": true,
       "requires": {
         "postcss": "^7.0.6",
       }
     },
     "postcss-modules-values": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz",
-      "integrity": "sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w==",
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz",
+      "integrity": "sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg==",
       "dev": true,
       "requires": {
-        "icss-replace-symbols": "^1.1.0",
+        "icss-utils": "^4.0.0",
         "postcss": "^7.0.6"
       }
     },
       }
     },
     "postcss-value-parser": {
-      "version": "3.3.1",
-      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
-      "integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz",
+      "integrity": "sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==",
       "dev": true
     },
     "prepend-http": {
       "dev": true
     },
     "process-nextick-args": {
-      "version": "1.0.7",
-      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
-      "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
       "dev": true
     },
     "promise-inflight": {
       "dev": true
     },
     "psl": {
-      "version": "1.1.32",
-      "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.32.tgz",
-      "integrity": "sha512-MHACAkHpihU/REGGPLj4sEfc/XKW2bheigvHO1dUqjaKigMp1C8+WLQYRGgeKFMsw5PMfegZcaN8IDXK/cD0+g==",
+      "version": "1.6.0",
+      "resolved": "https://registry.npmjs.org/psl/-/psl-1.6.0.tgz",
+      "integrity": "sha512-SYKKmVel98NCOYXpkwUqZqh0ahZeeKfmisiLIcEZdsb+WbLv02g/dI5BUmZnIyOe7RzZtLax81nnb2HbvC2tzA==",
       "dev": true
     },
     "public-encrypt": {
         "parse-asn1": "^5.0.0",
         "randombytes": "^2.0.1",
         "safe-buffer": "^5.1.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "pump": {
       }
     },
     "punycode": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
-      "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
+      "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
       "dev": true
     },
     "qs": {
       "requires": {
         "find-up": "^1.0.0",
         "read-pkg": "^1.0.0"
-      },
-      "dependencies": {
-        "find-up": {
-          "version": "1.1.2",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
-          "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
-          "dev": true,
-          "requires": {
-            "path-exists": "^2.0.0",
-            "pinkie-promise": "^2.0.0"
-          }
-        },
-        "path-exists": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
-          "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
-          "dev": true,
-          "requires": {
-            "pinkie-promise": "^2.0.0"
-          }
-        }
       }
     },
     "readable-stream": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
-      "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
+      "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
       "dev": true,
       "requires": {
         "core-util-is": "~1.0.0",
         "inherits": "~2.0.3",
         "isarray": "~1.0.0",
-        "process-nextick-args": "~1.0.6",
+        "process-nextick-args": "~2.0.0",
         "safe-buffer": "~5.1.1",
-        "string_decoder": "~1.0.3",
+        "string_decoder": "~1.1.1",
         "util-deprecate": "~1.0.1"
       }
     },
       "dev": true
     },
     "repeat-element": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz",
-      "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=",
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz",
+      "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==",
       "dev": true
     },
     "repeat-string": {
         "tough-cookie": "~2.4.3",
         "tunnel-agent": "^0.6.0",
         "uuid": "^3.3.2"
-      },
-      "dependencies": {
-        "safe-buffer": {
-          "version": "5.1.2",
-          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
-          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
-          "dev": true
-        }
       }
     },
     "require-directory": {
       "requires": {
         "expand-tilde": "^2.0.0",
         "global-modules": "^1.0.0"
+      },
+      "dependencies": {
+        "global-modules": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz",
+          "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==",
+          "dev": true,
+          "requires": {
+            "global-prefix": "^1.0.1",
+            "is-windows": "^1.0.1",
+            "resolve-dir": "^1.0.0"
+          }
+        }
       }
     },
     "resolve-from": {
       "dev": true
     },
     "rimraf": {
-      "version": "2.6.2",
-      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
-      "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+      "version": "2.7.1",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
+      "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
       "dev": true,
       "requires": {
-        "glob": "^7.0.5"
+        "glob": "^7.1.3"
       }
     },
     "ripemd160": {
       }
     },
     "safe-buffer": {
-      "version": "5.1.1",
-      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
-      "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
       "dev": true
     },
     "safe-regex": {
       }
     },
     "sass-loader": {
-      "version": "7.1.0",
-      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-7.1.0.tgz",
-      "integrity": "sha512-+G+BKGglmZM2GUSfT9TLuEp6tzehHPjAMoRRItOojWIqIGPloVCMhNIQuG639eJ+y033PaGTSjLaTHts8Kw79w==",
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-8.0.0.tgz",
+      "integrity": "sha512-+qeMu563PN7rPdit2+n5uuYVR0SSVwm0JsOUsaJXzgYcClWSlmX0iHDnmeOobPkf5kUglVot3QS6SyLyaQoJ4w==",
       "dev": true,
       "requires": {
-        "clone-deep": "^2.0.1",
-        "loader-utils": "^1.0.1",
-        "lodash.tail": "^4.1.1",
-        "neo-async": "^2.5.0",
-        "pify": "^3.0.0",
-        "semver": "^5.5.0"
+        "clone-deep": "^4.0.1",
+        "loader-utils": "^1.2.3",
+        "neo-async": "^2.6.1",
+        "schema-utils": "^2.1.0",
+        "semver": "^6.3.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "6.3.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
+          "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==",
+          "dev": true
+        }
       }
     },
     "schema-utils": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
-      "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+      "version": "2.6.1",
+      "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz",
+      "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==",
       "dev": true,
       "requires": {
-        "ajv": "^6.1.0",
-        "ajv-errors": "^1.0.0",
-        "ajv-keywords": "^3.1.0"
+        "ajv": "^6.10.2",
+        "ajv-keywords": "^3.4.1"
       }
     },
     "scss-tokenizer": {
       "dev": true
     },
     "serialize-javascript": {
-      "version": "1.7.0",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-1.7.0.tgz",
-      "integrity": "sha512-ke8UG8ulpFOxO8f8gRYabHQe/ZntKlcig2Mp+8+URDP1D8vJZ0KUt7LYo07q25Z/+JVSgpr/cui9PIp5H6/+nA==",
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz",
+      "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==",
       "dev": true
     },
     "set-blocking": {
       }
     },
     "shallow-clone": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-1.0.0.tgz",
-      "integrity": "sha512-oeXreoKR/SyNJtRJMAKPDSvd28OqEwG4eR/xc856cRGBII7gX9lvAqDxusPm0846z/w/hWYjI1NpKwJ00NHzRA==",
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+      "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
       "dev": true,
       "requires": {
-        "is-extendable": "^0.1.1",
-        "kind-of": "^5.0.0",
-        "mixin-object": "^2.0.1"
-      },
-      "dependencies": {
-        "kind-of": {
-          "version": "5.1.0",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz",
-          "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==",
-          "dev": true
-        }
+        "kind-of": "^6.0.2"
       }
     },
     "shebang-command": {
             "is-data-descriptor": "^1.0.0",
             "kind-of": "^6.0.2"
           }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==",
-          "dev": true
         }
       }
     },
       "dev": true,
       "requires": {
         "kind-of": "^3.2.0"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "sort-keys": {
       }
     },
     "sortablejs": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.9.0.tgz",
-      "integrity": "sha512-Ot6bYJ6PoqPmpsqQYXjn1+RKrY2NWQvQt/o4jfd/UYwVWndyO5EPO8YHbnm5HIykf8ENsm4JUrdAvolPT86yYA=="
+      "version": "1.10.1",
+      "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.1.tgz",
+      "integrity": "sha512-N6r7GrVmO8RW1rn0cTdvK3JR0BcqecAJ0PmYMCL3ZuqTH3pY+9QyqkmJSkkLyyDvd+AJnwaxTP22Ybr/83V9hQ=="
     },
     "source-list-map": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.0.tgz",
-      "integrity": "sha512-I2UmuJSRr/T8jisiROLU3A3ltr+swpniSmNPI4Ml3ZCX6tVnDsuZzK7F2hl5jTqbZBWCEKlj5HRQiPExXLgE8A==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz",
+      "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==",
       "dev": true
     },
     "source-map": {
       }
     },
     "source-map-support": {
-      "version": "0.5.12",
-      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz",
-      "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==",
+      "version": "0.5.16",
+      "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.16.tgz",
+      "integrity": "sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ==",
       "dev": true,
       "requires": {
         "buffer-from": "^1.0.0",
         "readable-stream": "^2.3.6",
         "to-arraybuffer": "^1.0.0",
         "xtend": "^4.0.0"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
       }
     },
     "stream-shift": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.0.tgz",
-      "integrity": "sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI=",
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz",
+      "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==",
       "dev": true
     },
     "strict-uri-encode": {
       }
     },
     "string_decoder": {
-      "version": "1.0.3",
-      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
-      "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
       "dev": true,
       "requires": {
         "safe-buffer": "~5.1.0"
       }
     },
     "style-loader": {
-      "version": "0.23.1",
-      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-0.23.1.tgz",
-      "integrity": "sha512-XK+uv9kWwhZMZ1y7mysB+zoihsEj4wneFWAS5qoiLwzW0WzSqMrrsIy+a3zkQJq0ipFtBpX5W3MqyRIBF/WFGg==",
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.1.1.tgz",
+      "integrity": "sha512-oIVF12trRq0od4Yojg7q0K3Lq/O6Ix/AYgVosykrVg+kWxxxUyk8KhKCCmekyGSUiVK1xxlAQymLWWdh6S9lOg==",
       "dev": true,
       "requires": {
-        "loader-utils": "^1.1.0",
-        "schema-utils": "^1.0.0"
+        "loader-utils": "^1.2.3",
+        "schema-utils": "^2.0.1"
       }
     },
     "supports-color": {
       }
     },
     "terser": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/terser/-/terser-4.0.0.tgz",
-      "integrity": "sha512-dOapGTU0hETFl1tCo4t56FN+2jffoKyER9qBGoUFyZ6y7WLoKT0bF+lAYi6B6YsILcGF3q1C2FBh8QcKSCgkgA==",
+      "version": "4.4.3",
+      "resolved": "https://registry.npmjs.org/terser/-/terser-4.4.3.tgz",
+      "integrity": "sha512-0ikKraVtRDKGzHrzkCv5rUNDzqlhmhowOBqC0XqUHFpW+vJ45+20/IFBcebwKfiS2Z9fJin6Eo+F1zLZsxi8RA==",
       "dev": true,
       "requires": {
-        "commander": "^2.19.0",
+        "commander": "^2.20.0",
         "source-map": "~0.6.1",
-        "source-map-support": "~0.5.10"
+        "source-map-support": "~0.5.12"
       }
     },
     "terser-webpack-plugin": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.3.0.tgz",
-      "integrity": "sha512-W2YWmxPjjkUcOWa4pBEv4OP4er1aeQJlSo2UhtCFQCuRXEHjOFscO8VyWHj9JLlA0RzQb8Y2/Ta78XZvT54uGg==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz",
+      "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==",
       "dev": true,
       "requires": {
-        "cacache": "^11.3.2",
-        "find-cache-dir": "^2.0.0",
+        "cacache": "^12.0.2",
+        "find-cache-dir": "^2.1.0",
         "is-wsl": "^1.1.0",
-        "loader-utils": "^1.2.3",
         "schema-utils": "^1.0.0",
-        "serialize-javascript": "^1.7.0",
+        "serialize-javascript": "^2.1.2",
         "source-map": "^0.6.1",
-        "terser": "^4.0.0",
-        "webpack-sources": "^1.3.0",
+        "terser": "^4.1.2",
+        "webpack-sources": "^1.4.0",
         "worker-farm": "^1.7.0"
       },
       "dependencies": {
-        "big.js": {
-          "version": "5.2.2",
-          "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
-          "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
-          "dev": true
-        },
-        "json5": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
-          "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
-          "dev": true,
-          "requires": {
-            "minimist": "^1.2.0"
-          }
-        },
-        "loader-utils": {
-          "version": "1.2.3",
-          "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
-          "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
+        "schema-utils": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
           "dev": true,
           "requires": {
-            "big.js": "^5.2.2",
-            "emojis-list": "^2.0.0",
-            "json5": "^1.0.1"
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
           }
-        },
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
-          "dev": true
         }
       }
     },
       "requires": {
         "readable-stream": "~2.3.6",
         "xtend": "~4.0.1"
-      },
-      "dependencies": {
-        "process-nextick-args": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz",
-          "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
-          "dev": true
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz",
-          "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==",
-          "dev": true,
-          "requires": {
-            "core-util-is": "~1.0.0",
-            "inherits": "~2.0.3",
-            "isarray": "~1.0.0",
-            "process-nextick-args": "~2.0.0",
-            "safe-buffer": "~5.1.1",
-            "string_decoder": "~1.1.1",
-            "util-deprecate": "~1.0.1"
-          }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
-          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
-          "dev": true,
-          "requires": {
-            "safe-buffer": "~5.1.0"
-          }
-        }
       }
     },
     "timers-browserify": {
-      "version": "2.0.10",
-      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.10.tgz",
-      "integrity": "sha512-YvC1SV1XdOUaL6gx5CoGroT3Gu49pK9+TZ38ErPldOWW4j49GI1HKs9DV+KGq/w6y+LZ72W1c8cKz2vzY+qpzg==",
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz",
+      "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==",
       "dev": true,
       "requires": {
         "setimmediate": "^1.0.4"
       "dev": true,
       "requires": {
         "kind-of": "^3.0.2"
+      },
+      "dependencies": {
+        "kind-of": {
+          "version": "3.2.2",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+          "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+          "dev": true,
+          "requires": {
+            "is-buffer": "^1.1.5"
+          }
+        }
       }
     },
     "to-regex": {
       "requires": {
         "psl": "^1.1.24",
         "punycode": "^1.4.1"
+      },
+      "dependencies": {
+        "punycode": {
+          "version": "1.4.1",
+          "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+          "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
+          "dev": true
+        }
       }
     },
     "trim-newlines": {
       }
     },
     "tslib": {
-      "version": "1.9.3",
-      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
-      "integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==",
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
+      "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==",
       "dev": true
     },
     "tty-browserify": {
       "dev": true
     },
     "uc.micro": {
-      "version": "1.0.5",
-      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.5.tgz",
-      "integrity": "sha512-JoLI4g5zv5qNyT09f4YAvEZIIV1oOjqnewYg5D38dkQljIzpPT296dbIGvKro3digYI1bkb7W6EP1y4uDlmzLg=="
-    },
-    "ultron": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.0.2.tgz",
-      "integrity": "sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po=",
-      "dev": true
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz",
+      "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA=="
     },
     "union-value": {
       "version": "1.0.1",
       }
     },
     "unique-slug": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.1.tgz",
-      "integrity": "sha512-n9cU6+gITaVu7VGj1Z8feKMmfAjEAQGhwD9fE3zvpRRa0wEIx8ODYkVGfSc94M2OX00tUFV8wH3zYbm1I8mxFg==",
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz",
+      "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==",
       "dev": true,
       "requires": {
         "imurmurhash": "^0.1.4"
       }
     },
     "upath": {
-      "version": "1.1.2",
-      "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz",
-      "integrity": "sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q==",
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz",
+      "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
       "dev": true
     },
     "uri-js": {
       "dev": true,
       "requires": {
         "punycode": "^2.1.0"
-      },
-      "dependencies": {
-        "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
-        }
       }
     },
     "urix": {
       "dev": true,
       "requires": {
         "inherits": "2.0.3"
+      },
+      "dependencies": {
+        "inherits": {
+          "version": "2.0.3",
+          "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+          "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
+          "dev": true
+        }
       }
     },
     "util-deprecate": {
       "dev": true
     },
     "uuid": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz",
-      "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==",
+      "version": "3.3.3",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz",
+      "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==",
       "dev": true
     },
     "v8-compile-cache": {
       }
     },
     "vm-browserify": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz",
-      "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=",
-      "dev": true,
-      "requires": {
-        "indexof": "0.0.1"
-      }
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz",
+      "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==",
+      "dev": true
     },
     "vue": {
-      "version": "2.6.10",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.10.tgz",
-      "integrity": "sha512-ImThpeNU9HbdZL3utgMCq0oiMzAkt1mcgy3/E6zWC/G6AaQoeuFdsl9nDhTDU3X1R6FK7nsIUuRACVcjI+A2GQ=="
+      "version": "2.6.11",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-2.6.11.tgz",
+      "integrity": "sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ=="
     },
     "vuedraggable": {
-      "version": "2.21.0",
-      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.21.0.tgz",
-      "integrity": "sha512-UDp0epjaZikuInoJA9rlEIJaSTQThabq0R9x7TqBdl0qGVFKKzo6glP6ubfzWBmV4iRIfbSOs2DV06s3B5h5tA==",
+      "version": "2.23.2",
+      "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.23.2.tgz",
+      "integrity": "sha512-PgHCjUpxEAEZJq36ys49HfQmXglattf/7ofOzUrW2/rRdG7tu6fK84ir14t1jYv4kdXewTEa2ieKEAhhEMdwkQ==",
       "requires": {
-        "sortablejs": "^1.9.0"
+        "sortablejs": "^1.10.1"
       }
     },
     "watchpack": {
         "chokidar": "^2.0.2",
         "graceful-fs": "^4.1.2",
         "neo-async": "^2.5.0"
-      },
-      "dependencies": {
-        "is-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
-        }
       }
     },
     "webpack": {
-      "version": "4.32.2",
-      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.32.2.tgz",
-      "integrity": "sha512-F+H2Aa1TprTQrpodRAWUMJn7A8MgDx82yQiNvYMaj3d1nv3HetKU0oqEulL9huj8enirKi8KvEXQ3QtuHF89Zg==",
+      "version": "4.41.4",
+      "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.41.4.tgz",
+      "integrity": "sha512-Lc+2uB6NjpCWsHI3trkoISOI64h9QYIXenbEWj3bn3oyjfB1lEBXjWAfAyY2sM0rZn41oD5V91OLwKRwS6Wp8Q==",
       "dev": true,
       "requires": {
         "@webassemblyjs/ast": "1.8.5",
         "@webassemblyjs/helper-module-context": "1.8.5",
         "@webassemblyjs/wasm-edit": "1.8.5",
         "@webassemblyjs/wasm-parser": "1.8.5",
-        "acorn": "^6.0.5",
-        "acorn-dynamic-import": "^4.0.0",
-        "ajv": "^6.1.0",
-        "ajv-keywords": "^3.1.0",
-        "chrome-trace-event": "^1.0.0",
+        "acorn": "^6.2.1",
+        "ajv": "^6.10.2",
+        "ajv-keywords": "^3.4.1",
+        "chrome-trace-event": "^1.0.2",
         "enhanced-resolve": "^4.1.0",
-        "eslint-scope": "^4.0.0",
+        "eslint-scope": "^4.0.3",
         "json-parse-better-errors": "^1.0.2",
-        "loader-runner": "^2.3.0",
-        "loader-utils": "^1.1.0",
-        "memory-fs": "~0.4.1",
-        "micromatch": "^3.1.8",
-        "mkdirp": "~0.5.0",
-        "neo-async": "^2.5.0",
-        "node-libs-browser": "^2.0.0",
+        "loader-runner": "^2.4.0",
+        "loader-utils": "^1.2.3",
+        "memory-fs": "^0.4.1",
+        "micromatch": "^3.1.10",
+        "mkdirp": "^0.5.1",
+        "neo-async": "^2.6.1",
+        "node-libs-browser": "^2.2.1",
         "schema-utils": "^1.0.0",
-        "tapable": "^1.1.0",
-        "terser-webpack-plugin": "^1.1.0",
-        "watchpack": "^1.5.0",
-        "webpack-sources": "^1.3.0"
+        "tapable": "^1.1.3",
+        "terser-webpack-plugin": "^1.4.3",
+        "watchpack": "^1.6.0",
+        "webpack-sources": "^1.4.1"
       },
       "dependencies": {
-        "is-accessor-descriptor": {
+        "schema-utils": {
           "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+          "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz",
+          "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==",
+          "dev": true,
           "requires": {
-            "kind-of": "^6.0.0"
+            "ajv": "^6.1.0",
+            "ajv-errors": "^1.0.0",
+            "ajv-keywords": "^3.1.0"
           }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
-          "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
-          }
-        },
-        "kind-of": {
-          "version": "6.0.2",
-          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz",
-          "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
         }
       }
     },
     "webpack-cli": {
-      "version": "3.3.2",
-      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.2.tgz",
-      "integrity": "sha512-FLkobnaJJ+03j5eplxlI0TUxhGCOdfewspIGuvDVtpOlrAuKMFC57K42Ukxqs1tn8947/PM6tP95gQc0DCzRYA==",
-      "dev": true,
-      "requires": {
-        "chalk": "^2.4.1",
-        "cross-spawn": "^6.0.5",
-        "enhanced-resolve": "^4.1.0",
-        "findup-sync": "^2.0.0",
-        "global-modules": "^1.0.0",
-        "import-local": "^2.0.0",
-        "interpret": "^1.1.0",
-        "loader-utils": "^1.1.0",
-        "supports-color": "^5.5.0",
-        "v8-compile-cache": "^2.0.2",
-        "yargs": "^12.0.5"
+      "version": "3.3.10",
+      "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-3.3.10.tgz",
+      "integrity": "sha512-u1dgND9+MXaEt74sJR4PR7qkPxXUSQ0RXYq8x1L6Jg1MYVEmGPrH6Ah6C4arD4r0J1P5HKjRqpab36k0eIzPqg==",
+      "dev": true,
+      "requires": {
+        "chalk": "2.4.2",
+        "cross-spawn": "6.0.5",
+        "enhanced-resolve": "4.1.0",
+        "findup-sync": "3.0.0",
+        "global-modules": "2.0.0",
+        "import-local": "2.0.0",
+        "interpret": "1.2.0",
+        "loader-utils": "1.2.3",
+        "supports-color": "6.1.0",
+        "v8-compile-cache": "2.0.3",
+        "yargs": "13.2.4"
       },
       "dependencies": {
         "ansi-regex": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
-          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz",
+          "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==",
           "dev": true
         },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          },
+          "dependencies": {
+            "supports-color": {
+              "version": "5.5.0",
+              "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+              "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+              "dev": true,
+              "requires": {
+                "has-flag": "^3.0.0"
+              }
+            }
+          }
+        },
         "cliui": {
-          "version": "4.1.0",
-          "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
-          "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==",
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+          "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
           "dev": true,
           "requires": {
-            "string-width": "^2.1.1",
-            "strip-ansi": "^4.0.0",
-            "wrap-ansi": "^2.0.0"
+            "string-width": "^3.1.0",
+            "strip-ansi": "^5.2.0",
+            "wrap-ansi": "^5.1.0"
           }
         },
         "cross-spawn": {
             "which": "^1.2.9"
           }
         },
+        "enhanced-resolve": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz",
+          "integrity": "sha512-F/7vkyTtyc/llOIn8oWclcB25KdRaiPBpZYDgJHgh/UHtpgT2p2eldQgtQnLtUvfMKPKxbRaQM/hHkvLHt1Vng==",
+          "dev": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "memory-fs": "^0.4.0",
+            "tapable": "^1.0.0"
+          }
+        },
+        "find-up": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^3.0.0"
+          }
+        },
+        "get-caller-file": {
+          "version": "2.0.5",
+          "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+          "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+          "dev": true
+        },
         "invert-kv": {
           "version": "2.0.0",
           "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
             "mem": "^4.0.0"
           }
         },
+        "require-main-filename": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+          "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+          "dev": true
+        },
         "string-width": {
-          "version": "2.1.1",
-          "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
-          "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",
+          "version": "3.1.0",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+          "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
           "dev": true,
           "requires": {
+            "emoji-regex": "^7.0.1",
             "is-fullwidth-code-point": "^2.0.0",
-            "strip-ansi": "^4.0.0"
+            "strip-ansi": "^5.1.0"
           }
         },
         "strip-ansi": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
-          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
+          "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==",
           "dev": true,
           "requires": {
-            "ansi-regex": "^3.0.0"
+            "ansi-regex": "^4.1.0"
+          }
+        },
+        "supports-color": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz",
+          "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
           }
         },
         "which-module": {
           "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=",
           "dev": true
         },
+        "wrap-ansi": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz",
+          "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.0",
+            "string-width": "^3.0.0",
+            "strip-ansi": "^5.0.0"
+          }
+        },
+        "y18n": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
+          "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
+          "dev": true
+        },
         "yargs": {
-          "version": "12.0.5",
-          "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz",
-          "integrity": "sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==",
+          "version": "13.2.4",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.2.4.tgz",
+          "integrity": "sha512-HG/DWAJa1PAnHT9JAhNa8AbAv3FPaiLzioSjCcmuXXhP8MlpHO5vwls4g4j6n30Z74GVQj8Xa62dWVx1QCGklg==",
           "dev": true,
           "requires": {
-            "cliui": "^4.0.0",
-            "decamelize": "^1.2.0",
+            "cliui": "^5.0.0",
             "find-up": "^3.0.0",
-            "get-caller-file": "^1.0.1",
-            "os-locale": "^3.0.0",
+            "get-caller-file": "^2.0.1",
+            "os-locale": "^3.1.0",
             "require-directory": "^2.1.1",
-            "require-main-filename": "^1.0.1",
+            "require-main-filename": "^2.0.0",
             "set-blocking": "^2.0.0",
-            "string-width": "^2.0.0",
+            "string-width": "^3.0.0",
             "which-module": "^2.0.0",
-            "y18n": "^3.2.1 || ^4.0.0",
-            "yargs-parser": "^11.1.1"
+            "y18n": "^4.0.0",
+            "yargs-parser": "^13.1.0"
           }
         },
         "yargs-parser": {
-          "version": "11.1.1",
-          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz",
-          "integrity": "sha512-C6kB/WJDiaxONLJQnF8ccx9SEeoTTLek8RVbaOIsrAUS8VrBEXfmeSnCZxygc+XC2sNMBIwOOnfcxiynjHsVSQ==",
+          "version": "13.1.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+          "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
           "dev": true,
           "requires": {
             "camelcase": "^5.0.0",
       }
     },
     "webpack-sources": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.3.0.tgz",
-      "integrity": "sha512-OiVgSrbGu7NEnEvQJJgdSFPl2qWKkWq5lHMhgiToIiN9w34EBnjYzSYs+VbL5KoYiLNtFFa7BZIKxRED3I32pA==",
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz",
+      "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==",
       "dev": true,
       "requires": {
         "source-list-map": "^2.0.0",
       "dev": true
     },
     "ws": {
-      "version": "1.1.5",
-      "resolved": "https://registry.npmjs.org/ws/-/ws-1.1.5.tgz",
-      "integrity": "sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w==",
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz",
+      "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==",
       "dev": true,
       "requires": {
-        "options": ">=0.0.5",
-        "ultron": "1.0.x"
+        "async-limiter": "~1.0.0"
       }
     },
     "xtend": {
-      "version": "4.0.1",
-      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
-      "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=",
+      "version": "4.0.2",
+      "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
       "dev": true
     },
     "y18n": {
index 0d1afbb0c05031a44f702399e00f0cfb6f8f5c63..8877cb9e8d332524eff4f7a1fb966191c789e458 100644 (file)
     "permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads"
   },
   "devDependencies": {
-    "css-loader": "^2.1.1",
-    "livereload": "^0.8.0",
-    "mini-css-extract-plugin": "^0.7.0",
-    "node-sass": "^4.12.0",
+    "css-loader": "^3.4.0",
+    "livereload": "^0.8.2",
+    "mini-css-extract-plugin": "^0.9.0",
+    "node-sass": "^4.13.0",
     "npm-run-all": "^4.1.5",
-    "sass-loader": "^7.1.0",
-    "style-loader": "^0.23.1",
-    "webpack": "^4.32.2",
-    "webpack-cli": "^3.3.2"
+    "sass-loader": "^8.0.0",
+    "style-loader": "^1.1.1",
+    "webpack": "^4.41.4",
+    "webpack-cli": "^3.3.10"
   },
   "dependencies": {
     "clipboard": "^2.0.4",
-    "codemirror": "^5.47.0",
+    "codemirror": "^5.50.0",
     "dropzone": "^5.5.1",
-    "markdown-it": "^8.4.2",
+    "markdown-it": "^10.0.0",
     "markdown-it-task-lists": "^2.1.1",
-    "sortablejs": "^1.9.0",
-    "vue": "^2.6.10",
-    "vuedraggable": "^2.21.0"
+    "sortablejs": "^1.10.1",
+    "vue": "^2.6.11",
+    "vuedraggable": "^2.23.2"
   },
   "browser": {
     "vue": "vue/dist/vue.common.js"
index 9f83e95ff238ea30a9672077b9fef6add7a885ab..546829247c6980b470e82eecf0dd0a523ef4efb2 100644 (file)
@@ -22,6 +22,7 @@
         <server name="APP_ENV" value="testing"/>
         <server name="APP_DEBUG" value="false"/>
         <server name="APP_LANG" value="en"/>
+        <server name="APP_THEME" value="none"/>
         <server name="APP_AUTO_LANG_PUBLIC" value="true"/>
         <server name="CACHE_DRIVER" value="array"/>
         <server name="SESSION_DRIVER" value="array"/>
@@ -48,5 +49,6 @@
         <server name="GOOGLE_SELECT_ACCOUNT" value=""/>
         <server name="APP_URL" value="http://bookstack.dev"/>
         <server name="DEBUGBAR_ENABLED" value="false"/>
+        <server name="SAML2_ENABLED" value="false"/>
     </php>
 </phpunit>
index ca90be3053af821b307e8b77bd72548f846056fa..fb26cede34b9a43894b1dceb4a83f90f4d2a20c9 100644 (file)
--- a/readme.md
+++ b/readme.md
@@ -107,22 +107,11 @@ The docker-compose setup runs an instance of [MailHog](https://github.com/mailho
 
 ## 🌎 Translations
 
-All text strings can be found in the `resources/lang` folder where each language option has its own folder. To add a new language you should copy the `en` folder to an new folder (eg. `fr` for french) then go through and translate all text strings in those files, leaving the keys and file-names intact. If a language string is missing then the `en` translation will be used. To show the language option in the user preferences language drop-down you will need to add your language to the options found at the bottom of the `resources/lang/en/settings.php` file. A system-wide language can also be set in the `.env` file like so: `APP_LANG=en`.
+Translations for text within BookStack is managed through the [BookStack project on Crowdin](https://crowdin.com/project/bookstack). Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time. Crowdin is the preferred way to provide translations, otherwise the raw translations files can be found within the `resources/lang` path.
 
-You will also need to add the language to the `locales` array in the `config/app.php` file.
+If you'd like a new language to be added to Crowdin, for you to be able to provide translations for, please [open a new issue here](https://github.com/BookStackApp/BookStack/issues/new?template=language_request.md).
 
-There is a script available which compares translation content to `en` files to see what items are missing or redundant. This can be ran like so from your BookStack install folder:
-
-```bash
-# Syntax
-php resources/lang/check.php <lang>
-
-# Examples
-php resources/lang/check.php fr
-php resources/lang/check.php pt_BR
-```
-
-Some strings have colon-prefixed variables in such as `:userName`. Leave these values as they are as they will be replaced at run-time.
+Please note, translations in BookStack are provided to the "Crowdin Global Translation Memory" which helps BookStack and other projects with finding translations. If you are not happy with contributing to this then providing translations to BookStack, even manually via GitHub, is not advised.
 
 ## 🎁 Contributing, Issues & Pull Requests
 
@@ -156,6 +145,8 @@ The BookStack source is provided under the MIT License. The libraries used by, a
 
 The great people that have worked to build and improve BookStack can [be seen here](https://github.com/BookStackApp/BookStack/graphs/contributors).
 
+The wonderful people that have provided translations, either through GitHub or via Crowdin [can be seen here](https://github.com/BookStackApp/BookStack/blob/master/.github/translators.txt).
+
 These are the great open-source projects used to help build BookStack:
 
 * [Laravel](http://laravel.com/)
@@ -174,4 +165,5 @@ These are the great open-source projects used to help build BookStack:
     * [Laravel IDE helper](https://github.com/barryvdh/laravel-ide-helper)
 * [WKHTMLtoPDF](http://wkhtmltopdf.org/index.html)
 * [Draw.io](https://github.com/jgraph/drawio)
-* [Laravel Stats](https://github.com/stefanzweifel/laravel-stats)
\ No newline at end of file
+* [Laravel Stats](https://github.com/stefanzweifel/laravel-stats)
+* [OneLogin's SAML PHP Toolkit](https://github.com/onelogin/php-saml)
\ No newline at end of file
diff --git a/resources/icons/fullscreen.svg b/resources/icons/fullscreen.svg
new file mode 100644 (file)
index 0000000..d00b574
--- /dev/null
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>
\ No newline at end of file
diff --git a/resources/icons/saml2.svg b/resources/icons/saml2.svg
new file mode 100644 (file)
index 0000000..a9a2994
--- /dev/null
@@ -0,0 +1,4 @@
+<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
+    <path d="M0 0h24v24H0z" fill="none"/>
+    <path d="M12.65 10C11.83 7.67 9.61 6 7 6c-3.31 0-6 2.69-6 6s2.69 6 6 6c2.61 0 4.83-1.67 5.65-4H17v4h4v-4h2v-4H12.65zM7 14c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2z"/>
+</svg>
\ No newline at end of file
index 464f394c1e7e42a8d1dc568c94568d53f1498819..a630f38f2a577591916f2e86796d8b796f4634a1 100644 (file)
@@ -12,8 +12,8 @@ class Collapsible {
         this.content = elem.querySelector('[collapsible-content]');
 
         if (!this.trigger) return;
-
         this.trigger.addEventListener('click', this.toggle.bind(this));
+        this.openIfContainsError();
     }
 
     open() {
@@ -36,6 +36,13 @@ class Collapsible {
         }
     }
 
+    openIfContainsError() {
+        const error = this.content.querySelector('.text-neg');
+        if (error) {
+            this.open();
+        }
+    }
+
 }
 
 export default Collapsible;
\ No newline at end of file
index 14cf08ae2da41014e4703c57bfae6f79c4dc1c9e..bbe059898af0ae937aa7ecf2f57f3a3e2f464ff9 100644 (file)
@@ -26,6 +26,7 @@ import permissionsTable from "./permissions-table";
 import customCheckbox from "./custom-checkbox";
 import bookSort from "./book-sort";
 import settingAppColorPicker from "./setting-app-color-picker";
+import settingColorPicker from "./setting-color-picker";
 import entityPermissionsEditor from "./entity-permissions-editor";
 import templateManager from "./template-manager";
 import newUserPassword from "./new-user-password";
@@ -59,6 +60,7 @@ const componentMapping = {
     'custom-checkbox': customCheckbox,
     'book-sort': bookSort,
     'setting-app-color-picker': settingAppColorPicker,
+    'setting-color-picker': settingColorPicker,
     'entity-permissions-editor': entityPermissionsEditor,
     'template-manager': templateManager,
     'new-user-password': newUserPassword,
index de256a8466a727add893f5370167f3db4ef34320..25d6bde47aa9771b2fef3fbff890f51121dd0809 100644 (file)
@@ -1,6 +1,7 @@
 import MarkdownIt from "markdown-it";
 import mdTasksLists from 'markdown-it-task-lists';
 import code from '../services/code';
+import Clipboard from "../services/clipboard";
 import {debounce} from "../services/util";
 
 import DrawIO from "../services/drawio";
@@ -75,6 +76,7 @@ class MarkdownEditor {
                 return;
             }
             if (action === 'insertDrawing') this.actionStartDrawing();
+            if (action === 'fullscreen') this.actionFullScreen();
         });
 
         // Mobile section toggling
@@ -215,20 +217,16 @@ class MarkdownEditor {
 
         // Handle image paste
         cm.on('paste', (cm, event) => {
-            const clipboardItems = event.clipboardData.items;
-            if (!event.clipboardData || !clipboardItems) return;
+            const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-            // Don't handle if clipboard includes text content
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes('text/')) {
-                    return;
-                }
+            // Don't handle the event ourselves if no items exist of contains table-looking data
+            if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+                return;
             }
 
-            for (let clipboardItem of clipboardItems) {
-                if (clipboardItem.type.includes("image")) {
-                    uploadImage(clipboardItem.getAsFile());
-                }
+            const images = clipboard.getImages();
+            for (const image of images) {
+                uploadImage(image);
             }
         });
 
@@ -246,13 +244,15 @@ class MarkdownEditor {
                 });
             }
 
-            if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
+            const clipboard = new Clipboard(event.dataTransfer);
+            if (clipboard.hasItems()) {
                 const cursorPos = cm.coordsChar({left: event.pageX, top: event.pageY});
                 cm.setCursor(cursorPos);
                 event.stopPropagation();
                 event.preventDefault();
-                for (let i = 0; i < event.dataTransfer.files.length; i++) {
-                    uploadImage(event.dataTransfer.files[i]);
+                const images = clipboard.getImages();
+                for (const image of images) {
+                    uploadImage(image);
                 }
             }
 
@@ -481,6 +481,13 @@ class MarkdownEditor {
         });
     }
 
+    // Make the editor full screen
+    actionFullScreen() {
+        const alreadyFullscreen = this.elem.classList.contains('fullscreen');
+        this.elem.classList.toggle('fullscreen', !alreadyFullscreen);
+        document.body.classList.toggle('markdown-fullscreen', !alreadyFullscreen);
+    }
+
     // Scroll to a specified text
     scrollToText(searchText) {
         if (!searchText) {
index ad6a01061ec033c0beaebc33fa003a665b57b0d7..6963ba9d155f842f434dbe4d6a07877d230b2a05 100644 (file)
@@ -1,3 +1,4 @@
+import {fadeIn, fadeOut} from "../services/animations";
 
 class Overlay {
 
@@ -19,29 +20,15 @@ class Overlay {
         }
     }
 
-    hide() { this.toggle(false); }
-    show() { this.toggle(true); }
-
-    toggle(show = true) {
-        let start = Date.now();
-        let duration = 240;
-
-        function setOpacity() {
-            let elapsedTime = (Date.now() - start);
-            let targetOpacity = show ? (elapsedTime / duration) : 1-(elapsedTime / duration);
-            this.container.style.opacity = targetOpacity;
-            if (elapsedTime > duration) {
-                this.container.style.display = show ? 'flex' : 'none';
-                if (show) {
-                    this.focusOnBody();
-                }
-                this.container.style.opacity = '';
-            } else {
-                requestAnimationFrame(setOpacity.bind(this));
-            }
-        }
+    hide(onComplete = null) { this.toggle(false, onComplete); }
+    show(onComplete = null) { this.toggle(true, onComplete); }
 
-        requestAnimationFrame(setOpacity.bind(this));
+    toggle(show = true, onComplete) {
+        if (show) {
+            fadeIn(this.container, 240, onComplete);
+        } else {
+            fadeOut(this.container, 240, onComplete);
+        }
     }
 
     focusOnBody() {
index 6c0c0b31dcecb86fbf39fea7c6dbecb496d1822f..ee894c9325c5c5fc0bef05f684a2e6e8032f3661 100644 (file)
@@ -6,11 +6,16 @@ class SettingAppColorPicker {
         this.colorInput = elem.querySelector('input[type=color]');
         this.lightColorInput = elem.querySelector('input[name="setting-app-color-light"]');
         this.resetButton = elem.querySelector('[setting-app-color-picker-reset]');
+        this.defaultButton = elem.querySelector('[setting-app-color-picker-default]');
 
         this.colorInput.addEventListener('change', this.updateColor.bind(this));
         this.colorInput.addEventListener('input', this.updateColor.bind(this));
         this.resetButton.addEventListener('click', event => {
-            this.colorInput.value = '#206ea7';
+            this.colorInput.value = this.colorInput.dataset.current;
+            this.updateColor();
+        });
+        this.defaultButton.addEventListener('click', event => {
+            this.colorInput.value = this.colorInput.dataset.default;
             this.updateColor();
         });
     }
@@ -53,4 +58,4 @@ class SettingAppColorPicker {
 
 }
 
-export default SettingAppColorPicker;
\ No newline at end of file
+export default SettingAppColorPicker;
diff --git a/resources/js/components/setting-color-picker.js b/resources/js/components/setting-color-picker.js
new file mode 100644 (file)
index 0000000..4d8ce0f
--- /dev/null
@@ -0,0 +1,18 @@
+
+class SettingColorPicker {
+
+    constructor(elem) {
+        this.elem = elem;
+        this.colorInput = elem.querySelector('input[type=color]');
+        this.resetButton = elem.querySelector('[setting-color-picker-reset]');
+        this.defaultButton = elem.querySelector('[setting-color-picker-default]');
+        this.resetButton.addEventListener('click', event => {
+            this.colorInput.value = this.colorInput.dataset.current;
+        });
+        this.defaultButton.addEventListener('click', event => {
+          this.colorInput.value = this.colorInput.dataset.default;
+        });
+    }
+}
+
+export default SettingColorPicker;
index 60a6743ea7679bdebb7100bd2c047e1b26101166..b9e3340a8275358db491fa9e257354be5631be51 100644 (file)
@@ -1,5 +1,6 @@
 import Code from "../services/code";
 import DrawIO from "../services/drawio";
+import Clipboard from "../services/clipboard";
 
 /**
  * Handle pasting images from clipboard.
@@ -8,30 +9,33 @@ import DrawIO from "../services/drawio";
  * @param editor
  */
 function editorPaste(event, editor, wysiwygComponent) {
-    const clipboardItems = event.clipboardData.items;
-    if (!event.clipboardData || !clipboardItems) return;
+    const clipboard = new Clipboard(event.clipboardData || event.dataTransfer);
 
-    // Don't handle if clipboard includes text content
-    for (let clipboardItem of clipboardItems) {
-        if (clipboardItem.type.includes('text/')) {
-            return;
-        }
+    // Don't handle the event ourselves if no items exist of contains table-looking data
+    if (!clipboard.hasItems() || clipboard.containsTabularData()) {
+        return;
     }
 
-    for (let clipboardItem of clipboardItems) {
-        if (!clipboardItem.type.includes("image")) {
-            continue;
-        }
+    const images = clipboard.getImages();
+    for (const imageFile of images) {
 
         const id = "image-" + Math.random().toString(16).slice(2);
         const loadingImage = window.baseUrl('/loading.gif');
-        const file = clipboardItem.getAsFile();
+        event.preventDefault();
 
         setTimeout(() => {
             editor.insertContent(`<p><img src="${loadingImage}" id="${id}"></p>`);
 
-            uploadImageFile(file, wysiwygComponent).then(resp => {
-                editor.dom.setAttrib(id, 'src', resp.thumbs.display);
+            uploadImageFile(imageFile, wysiwygComponent).then(resp => {
+                const safeName = resp.name.replace(/"/g, '');
+                const newImageHtml = `<img src="${resp.thumbs.display}" alt="${safeName}" />`;
+
+                const newEl = editor.dom.create('a', {
+                    target: '_blank',
+                    href: resp.url,
+                }, newImageHtml);
+
+                editor.dom.replace(newEl, id);
             }).catch(err => {
                 editor.dom.remove(id);
                 window.$events.emit('error', trans('errors.image_upload_error'));
@@ -160,7 +164,7 @@ function codePlugin() {
             let cmInstance = editorElem.CodeMirror;
             if (cmInstance) {
                 Code.setContent(cmInstance, code);
-                Code.setMode(cmInstance, lang);
+                Code.setMode(cmInstance, lang, code);
             }
             let textArea = selectedNode.querySelector('textarea');
             if (textArea) textArea.textContent = code;
@@ -634,6 +638,10 @@ class WysiwygEditor {
                         });
                     }
 
+                    if (!event.isDefaultPrevented()) {
+                        editorPaste(event, editor, context);
+                    }
+
                     wrap = null;
                 });
 
index b6158ea5f8fbee97872d6350f0a8fa72e9954c13..278a765d5802e2af4feefd1fb1cb26aeaee9b805 100644 (file)
@@ -5,6 +5,22 @@
  */
 const animateStylesCleanupMap = new WeakMap();
 
+/**
+ * Fade in the given element.
+ * @param {Element} element
+ * @param {Number} animTime
+ * @param {Function|null} onComplete
+ */
+export function fadeIn(element, animTime = 400, onComplete = null) {
+    cleanupExistingElementAnimation(element);
+    element.style.display = 'block';
+    animateStyles(element, {
+        opacity: ['0', '1']
+    }, animTime, () => {
+        if (onComplete) onComplete();
+    });
+}
+
 /**
  * Fade out the given element.
  * @param {Element} element
diff --git a/resources/js/services/clipboard.js b/resources/js/services/clipboard.js
new file mode 100644 (file)
index 0000000..da921e5
--- /dev/null
@@ -0,0 +1,54 @@
+
+class Clipboard {
+
+    /**
+     * Constructor
+     * @param {DataTransfer} clipboardData
+     */
+    constructor(clipboardData) {
+        this.data = clipboardData;
+    }
+
+    /**
+     * Check if the clipboard has any items.
+     */
+    hasItems() {
+        return Boolean(this.data) && Boolean(this.data.types) && this.data.types.length > 0;
+    }
+
+    /**
+     * Check if the given event has tabular-looking data in the clipboard.
+     * @return {boolean}
+     */
+    containsTabularData() {
+        const rtfData = this.data.getData( 'text/rtf');
+        return rtfData && rtfData.includes('\\trowd');
+    }
+
+    /**
+     * Get the images that are in the clipboard data.
+     * @return {Array<File>}
+     */
+    getImages() {
+        const types = this.data.types;
+        const files = this.data.files;
+        const images = [];
+
+        for (const type of types) {
+            if (type.includes('image')) {
+                const item = this.data.getData(type);
+                images.push(item.getAsFile());
+            }
+        }
+
+        for (const file of files) {
+            if (file.type.includes('image')) {
+                images.push(file);
+            }
+        }
+
+        return images;
+    }
+}
+
+export default Clipboard;
\ No newline at end of file
index f69f28b8e96f7db6498260681925153acc49f6a0..93c3e431f4f5baf7482c898539599f4f8ad2e3e8 100644 (file)
@@ -29,8 +29,9 @@ import 'codemirror/mode/yaml/yaml';
 // Addons
 import 'codemirror/addon/scroll/scrollpastend';
 
-// Mapping of potential languages or formats from user input
-// to their proper codemirror modes.
+// Mapping of possible languages or formats from user input to their codemirror modes.
+// Value can be a mode string or a function that will receive the code content & return the mode string.
+// The function option is used in the event the exact mode could be dynamic depending on the code.
 const modeMap = {
     css: 'css',
     c: 'text/x-csrc',
@@ -60,7 +61,9 @@ const modeMap = {
     powershell: 'powershell',
     properties: 'properties',
     ocaml: 'mllike',
-    php: 'php',
+    php: (content) => {
+        return content.includes('<?php') ? 'php' : 'text/x-php';
+    },
     py: 'python',
     python: 'python',
     ruby: 'ruby',
@@ -92,16 +95,17 @@ function highlight() {
  * @param {HTMLElement} elem
  */
 function highlightElem(elem) {
-    let innerCodeElem = elem.querySelector('code[class^=language-]');
+    const innerCodeElem = elem.querySelector('code[class^=language-]');
+    elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
+    const content = elem.textContent;
+
     let mode = '';
     if (innerCodeElem !== null) {
-        let langName = innerCodeElem.className.replace('language-', '');
-        mode = getMode(langName);
+        const langName = innerCodeElem.className.replace('language-', '');
+        mode = getMode(langName, content);
     }
-    elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    let content = elem.textContent.trim();
 
-    let cm = CodeMirror(function(elt) {
+    const cm = CodeMirror(function(elt) {
         elem.parentNode.replaceChild(elt, elem);
     }, {
         value: content,
@@ -142,12 +146,24 @@ function addCopyIcon(cmInstance) {
 
 /**
  * Search for a codemirror code based off a user suggestion
- * @param suggestion
+ * @param {String} suggestion
+ * @param {String} content
  * @returns {string}
  */
-function getMode(suggestion) {
+function getMode(suggestion, content) {
     suggestion = suggestion.trim().replace(/^\./g, '').toLowerCase();
-    return (typeof modeMap[suggestion] !== 'undefined') ? modeMap[suggestion] : '';
+
+    const modeMapType = typeof modeMap[suggestion];
+
+    if (modeMapType === 'undefined') {
+        return '';
+    }
+
+    if (modeMapType === 'function') {
+        return modeMap[suggestion](content);
+    }
+
+    return modeMap[suggestion];
 }
 
 /**
@@ -165,8 +181,8 @@ function getTheme() {
  * @returns {{wrap: Element, editor: *}}
  */
 function wysiwygView(elem) {
-    let doc = elem.ownerDocument;
-    let codeElem = elem.querySelector('code');
+    const doc = elem.ownerDocument;
+    const codeElem = elem.querySelector('code');
 
     let lang = (elem.className || '').replace('language-', '');
     if (lang === '' && codeElem) {
@@ -174,9 +190,9 @@ function wysiwygView(elem) {
     }
 
     elem.innerHTML = elem.innerHTML.replace(/<br\s*[\/]?>/gi ,'\n');
-    let content = elem.textContent;
-    let newWrap = doc.createElement('div');
-    let newTextArea = doc.createElement('textarea');
+    const content = elem.textContent;
+    const newWrap = doc.createElement('div');
+    const newTextArea = doc.createElement('textarea');
 
     newWrap.className = 'CodeMirrorContainer';
     newWrap.setAttribute('data-lang', lang);
@@ -192,7 +208,7 @@ function wysiwygView(elem) {
         newWrap.appendChild(elt);
     }, {
         value: content,
-        mode:  getMode(lang),
+        mode:  getMode(lang, content),
         lineNumbers: true,
         lineWrapping: false,
         theme: getTheme(),
@@ -211,14 +227,14 @@ function wysiwygView(elem) {
  * @returns {*}
  */
 function popupEditor(elem, modeSuggestion) {
-    let content = elem.textContent;
+    const content = elem.textContent;
 
     return CodeMirror(function(elt) {
         elem.parentNode.insertBefore(elt, elem);
         elem.style.display = 'none';
     }, {
         value: content,
-        mode:  getMode(modeSuggestion),
+        mode:  getMode(modeSuggestion, content),
         lineNumbers: true,
         lineWrapping: false,
         theme: getTheme()
@@ -230,8 +246,8 @@ function popupEditor(elem, modeSuggestion) {
  * @param cmInstance
  * @param modeSuggestion
  */
-function setMode(cmInstance, modeSuggestion) {
-      cmInstance.setOption('mode', getMode(modeSuggestion));
+function setMode(cmInstance, modeSuggestion, content) {
+      cmInstance.setOption('mode', getMode(modeSuggestion, content));
 }
 
 /**
@@ -242,10 +258,18 @@ function setMode(cmInstance, modeSuggestion) {
 function setContent(cmInstance, codeContent) {
     cmInstance.setValue(codeContent);
     setTimeout(() => {
-        cmInstance.refresh();
+        updateLayout(cmInstance);
     }, 10);
 }
 
+/**
+ * Update the layout (codemirror refresh) of a cm instance.
+ * @param cmInstance
+ */
+function updateLayout(cmInstance) {
+    cmInstance.refresh();
+}
+
 /**
  * Get a CodeMirror instance to use for the markdown editor.
  * @param {HTMLElement} elem
@@ -285,6 +309,7 @@ export default {
     popupEditor: popupEditor,
     setMode: setMode,
     setContent: setContent,
+    updateLayout: updateLayout,
     markdownEditor: markdownEditor,
     getMetaKey: getMetaKey,
 };
index c6df6b1a5de43aafc902fe45fc4a454f2a3ee2dd..48b4e1766b9ba0c4a84919a7dc2ee097c25b248f 100644 (file)
@@ -3,13 +3,15 @@ import codeLib from "../services/code";
 const methods = {
     show() {
         if (!this.editor) this.editor = codeLib.popupEditor(this.$refs.editor, this.language);
-        this.$refs.overlay.components.overlay.show();
+        this.$refs.overlay.components.overlay.show(() => {
+            codeLib.updateLayout(this.editor);
+        });
     },
     hide() {
         this.$refs.overlay.components.overlay.hide();
     },
     updateEditorMode(language) {
-        codeLib.setMode(this.editor, language);
+        codeLib.setMode(this.editor, language, this.editor.getValue());
     },
     updateLanguage(lang) {
         this.language = lang;
index ccec60561138cfc526bbe7d2dac846c2305badf6..ff2bf8653d85f59cc3e9e976062fd4295e7127e9 100644 (file)
@@ -17,6 +17,8 @@ return [
     'ldap_fail_authed' => 'LDAP-Zugriff mit DN und Passwort ist fehlgeschlagen',
     'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert.',
     'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
+    'saml_already_logged_in' => 'Sie sind bereits angemeldet',
+    'saml_user_not_registered' => 'Kein Benutzer mit ID :name registriert und die automatische Registrierung ist deaktiviert',
     'social_no_action_defined' => 'Es ist keine Aktion definiert',
     'social_login_bad_response' => "Fehler bei der :socialAccount-Anmeldung: \n:error",
     'social_account_in_use' => 'Dieses :socialAccount-Konto wird bereits verwendet. Bitte melden Sie sich mit dem :socialAccount-Konto an.',
index 9b5b5166b810d40cab20f4f94e40cc0a26b3e7fa..e6235015660218d3c65faa587c90246ead863d1a 100644 (file)
@@ -9,16 +9,11 @@ return [
     'permissionJson' => 'Du hast keine Berechtigung, die angeforderte Aktion auszuführen.',
 
     // Auth
+    'saml_already_logged_in' => 'Du bist bereits angemeldet',
     'error_user_exists_different_creds' => 'Ein Benutzer mit der E-Mail-Adresse :email ist bereits mit anderen Anmeldedaten registriert.',
     'email_already_confirmed' => 'Die E-Mail-Adresse ist bereits bestätigt. Bitte melde dich an.',
     'email_confirmation_invalid' => 'Der Bestätigungslink ist nicht gültig oder wurde bereits verwendet. Bitte registriere dich erneut.',
-    'email_confirmation_expired' => 'Der Bestätigungslink ist abgelaufen. Es wurde eine neue Bestätigungs-E-Mail gesendet.',
-    'ldap_fail_anonymous' => 'Anonymer LDAP-Zugriff ist fehlgeschlafgen',
-    'ldap_fail_authed' => 'LDAP-Zugriff mit DN und Passwort ist fehlgeschlagen',
-    'ldap_extension_not_installed' => 'LDAP-PHP-Erweiterung ist nicht installiert.',
-    'ldap_cannot_connect' => 'Die Verbindung zum LDAP-Server ist fehlgeschlagen. Beim initialen Verbindungsaufbau trat ein Fehler auf.',
-    'social_no_action_defined' => 'Es ist keine Aktion definiert',
-    'social_login_bad_response' => "Fehler bei der :socialAccount-Anmeldung: \n:error",
+
     'social_account_in_use' => 'Dieses :socialAccount-Konto wird bereits verwendet. Bitte melde dich mit dem :socialAccount-Konto an.',
     'social_account_email_in_use' => 'Die E-Mail-Adresse ":email" ist bereits registriert. Wenn Du bereits registriert bist, kannst Du Dein :socialAccount-Konto in Deinen Profil-Einstellungen verknüpfen.',
     'social_account_existing' => 'Dieses :socialAccount-Konto ist bereits mit Ihrem Profil verknüpft.',
index 1807217a375d2f0a77f5fe10ea80113ee57ea490..c8b4a2b223e3afb602017a5ad43d9b5193a1dc02 100644 (file)
@@ -38,6 +38,7 @@ return [
     'reset' => 'Reset',
     'remove' => 'Remove',
     'add' => 'Add',
+    'fullscreen' => 'Fullscreen',
 
     // Sort Options
     'sort_options' => 'Sort Options',
index c3b47744d31f5fc63081ec6d50136511721941c1..a7c591c5d2f414fc76d1f110b36125cb39e7c81d 100644 (file)
@@ -17,6 +17,12 @@ return [
     'ldap_fail_authed' => 'LDAP access failed using given dn & password details',
     'ldap_extension_not_installed' => 'LDAP PHP extension not installed',
     'ldap_cannot_connect' => 'Cannot connect to ldap server, Initial connection failed',
+    'saml_already_logged_in' => 'Already logged in',
+    'saml_user_not_registered' => 'The user :name is not registered and automatic registration is disabled',
+    'saml_no_email_address' => 'Could not find an email address, for this user, in the data provided by the external authentication system',
+    'saml_invalid_response_id' => 'The request from the external authentication system is not recognised by a process started by this application. Navigating back after a login could cause this issue.',
+    'saml_fail_authed' => 'Login using :system failed, system did not provide successful authorization',
+    'saml_email_exists' => 'Registration unsuccessful since a user already exists with email address ":email"',
     'social_no_action_defined' => 'No action defined',
     'social_login_bad_response' => "Error received during :socialAccount login: \n:error",
     'social_account_in_use' => 'This :socialAccount account is already in use, Try logging in via the :socialAccount option.',
index 56e0868e4b80dc4830a8966f7122a0c4ae969357..6be7cc9cb18a0f0ab30c373f84dd1b0d5a3a1004 100755 (executable)
@@ -33,7 +33,7 @@ return [
     'app_logo' => 'Application Logo',
     'app_logo_desc' => 'This image should be 43px in height. <br>Large images will be scaled down.',
     'app_primary_color' => 'Application Primary Color',
-    'app_primary_color_desc' => 'This should be a hex value. <br>Leave empty to reset to the default color.',
+    'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.',
     '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',
@@ -41,12 +41,22 @@ return [
     'app_disable_comments_toggle' => 'Disable comments',
     'app_disable_comments_desc' => 'Disables comments across all pages in the application. <br> Existing comments are not shown.',
 
+    // Color settings
+    'content_colors' => 'Content Colors',
+    'content_colors_desc' => 'Sets colors for all elements in the page organisation hierarchy. Choosing colors with a similar brightness to the default colors is recommended for readability.',
+    'bookshelf_color' => 'Shelf Color',
+    'book_color' => 'Book Color',
+    'chapter_color' => 'Chapter Color',
+    'page_color' => 'Page Color',
+    'page_draft_color' => 'Page Draft Color',
+
     // Registration Settings
     'reg_settings' => 'Registration',
     'reg_enable' => 'Enable Registration',
     'reg_enable_toggle' => 'Enable registration',
     'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
     'reg_default_role' => 'Default user role after registration',
+    'reg_enable_ldap_warning' => 'The option above is not used while LDAP authentication is active. User accounts for non-existing members will be auto-created if authentication, against the LDAP system in use, is successful.',
     'reg_email_confirmation' => 'Email Confirmation',
     'reg_email_confirmation_toggle' => 'Require email confirmation',
     'reg_confirm_email_desc' => 'If domain restriction is used then email confirmation will be required and this option will be ignored.',
index 5baeed3c8e456ded7d9eb7bb5a1bd941adf6cd63..c3fedeb45ac8ec0c1f2698c8a4495629e065b153 100644 (file)
@@ -6,43 +6,43 @@
 return [
 
     // Pages
-    'page_create'                 => '페이지 생성',
-    'page_create_notification'    => '페이지를 만들었습니다.',
-    'page_update'                 => '페이지 업데이트',
-    'page_update_notification'    => '페이지를 업데이트하였습니다.',
-    'page_delete'                 => '페이지 삭제',
-    'page_delete_notification'    => '페이지를 삭제하였습니다.',
-    'page_restore'                => '페이지 복원',
-    'page_restore_notification'   => '페이지를 복원하였습니다.',
-    'page_move'                   => '페이지 이동',
+    'page_create'                 => '문서 만들기',
+    'page_create_notification'    => '문서 만듦',
+    'page_update'                 => '문서 수정',
+    'page_update_notification'    => '문서 수정함',
+    'page_delete'                 => '문서 지우기',
+    'page_delete_notification'    => '문서 지움',
+    'page_restore'                => '문서 복원',
+    'page_restore_notification'   => '문서 복원함',
+    'page_move'                   => '문서 옮기기',
 
     // Chapters
     'chapter_create'              => '챕터 만들기',
-    'chapter_create_notification' => '챕터를 만들었습니다.',
-    'chapter_update'              => '챕터 업데이트',
-    'chapter_update_notification' => '챕터를 업데이트하였습니다.',
-    'chapter_delete'              => 'ì±\95í\84° ì\82­ì \9c',
-    'chapter_delete_notification' => 'ì±\94í\84°ë¥¼ ì\82­ì \9cí\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'chapter_move'                => 'ì±\95í\84° ì\9d´ë\8f\99',
+    'chapter_create_notification' => '챕터 만듦',
+    'chapter_update'              => '챕터 바꾸기',
+    'chapter_update_notification' => '챕터 바꿈',
+    'chapter_delete'              => 'ì±\95í\84° ì§\80ì\9a°ê¸°',
+    'chapter_delete_notification' => 'ì±\95í\84° ì§\80ì\9b\80',
+    'chapter_move'                => 'ì±\95í\84° ì\98®ê¸°ê¸°',
 
     // Books
-    'book_create'                 => '책 만들기',
-    'book_create_notification'    => 'ì±\85ì\9d\84 ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'book_update'                 => '책 업데이트',
-    'book_update_notification'    => 'ì±\85ì\9d\84 ì\97\85ë\8d°ì\9d´í\8a¸í\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'book_delete'                 => '책 삭제',
-    'book_delete_notification'    => 'ì±\85ì\9d\84 ì\82­ì \9cí\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
-    'book_sort'                   => '책 정렬',
-    'book_sort_notification'      => 'ì±\85ì\9d\84 ì \95ë ¬í\95\98ì\98\80ì\8aµë\8b\88ë\8b¤.',
+    'book_create'                 => '책 만들기',
+    'book_create_notification'    => 'ì±\85ì\9e\90 ë§\8cë\93¦',
+    'book_update'                 => '책자 바꾸기',
+    'book_update_notification'    => 'ì±\85ì\9e\90 ë°\94ê¿\88',
+    'book_delete'                 => '책자 지우기',
+    'book_delete_notification'    => 'ì±\85ì\9e\90 ì§\80ì\9b\80',
+    'book_sort'                   => '책 정렬',
+    'book_sort_notification'      => 'ì±\85ì\9e\90 ì \95ë ¬í\95¨',
 
     // Bookshelves
-    'bookshelf_create'            => 'created Bookshelf',
-    'bookshelf_create_notification'    => 'Bookshelf Successfully Created',
-    'bookshelf_update'                 => 'updated bookshelf',
-    'bookshelf_update_notification'    => 'Bookshelf Successfully Updated',
-    'bookshelf_delete'                 => 'deleted bookshelf',
-    'bookshelf_delete_notification'    => 'Bookshelf Successfully Deleted',
+    'bookshelf_create'            => '서가 만들기',
+    'bookshelf_create_notification'    => '서가 만듦',
+    'bookshelf_update'                 => '서가 바꾸기',
+    'bookshelf_update_notification'    => '서가 바꿈',
+    'bookshelf_delete'                 => '서가 지우기',
+    'bookshelf_delete_notification'    => '서가 지움',
 
     // Other
-    'commented_on'                => 'commented on',
+    'commented_on'                => '댓글 쓰기',
 ];
index 86b55dc2c860ade5a6c87983efc36779d0dc2a77..9701bdf703cb7698a34f65933a74ae3c2ba00038 100644 (file)
@@ -6,72 +6,72 @@
  */
 return [
 
-    'failed' => '이 자격 증명은 등록되어 있지 않습니다.',
-    'throttle' => '로그인 시도 횟수 제한을 초과했습니다. :seconds초 후에 다시 시도하십시오.',
+    'failed' => '가입하지 않았거나 비밀번호가 틀립니다.',
+    'throttle' => '여러 번 실패했습니다. :seconds초 후에 다시 시도하세요.',
 
     // Login & Register
-    'sign_up' => '신규등록',
+    'sign_up' => '가입',
     'log_in' => '로그인',
-    'log_in_with' => ':socialDriver 로그인',
-    'sign_up_with' => ':socialDriver로 등록',
+    'log_in_with' => ':socialDriver 로그인',
+    'sign_up_with' => ':socialDriver로 가입',
     'logout' => '로그아웃',
 
     'name' => '이름',
-    'username' => '사용자이름',
-    'email' => '이메일',
+    'username' => '사용자 이름',
+    'email' => '메일 주소',
     'password' => '비밀번호',
-    'password_confirm' => '비밀번호 (확인)',
-    'password_hint' => '7자 이상이어야 합니다.',
-    'forgot_password' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ì\9e\8aì\9c¼ì\85¨ì\8aµë\8b\88ê¹\8c?',
-    'remember_me' => '자동로그인',
-    'ldap_email_hint' => '이 계정에서 사용하는 이메일을 입력해 주세요.',
-    'create_account' => 'ê³\84ì \95 ë§\8cë\93¤ê¸°',
-    'already_have_account' => 'Already have an account?',
-    'dont_have_account' => 'Don\'t have an account?',
-    'social_login' => 'SNS로그인',
-    'social_registration' => 'SNS등록',
-    'social_registration_text' => '다른 서비스를 사용하여 등록하고 로그인.',
+    'password_confirm' => '비밀번호 확인',
+    'password_hint' => '일곱 글자를 넘어야 합니다.',
+    'forgot_password' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ì\9e\8aì\97\88ë\82\98ì\9a\94?',
+    'remember_me' => '로그인 유지',
+    'ldap_email_hint' => '이 계정에 대한 메일 주소를 입력하세요.',
+    'create_account' => 'ê°\80ì\9e\85',
+    'already_have_account' => '계정이 있나요?',
+    'dont_have_account' => '계정이 없나요?',
+    'social_login' => '소셜 로그인',
+    'social_registration' => '소셜 가입',
+    'social_registration_text' => '소셜 계정으로 가입하고 로그인합니다.',
 
-    'register_thanks' => '등록이완료되었습니다!',
-    'register_confirm' => 'ë\8b¹ì\8b ì\9d\98 ì\9d´ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ì\8b í\9b\84 í\99\95ì\9d¸ ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ :appNameì\97\90 ì\95¡ì\84¸ì\8a¤í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'registrations_disabled' => '현재 등록이 불가합니다.',
-    'registration_email_domain_invalid' => '해당 이메일 도메인으로 액세스 할 수 없습니다.',
-    'register_success' => '등록을 완료하고 로그인 할 수 있습니다!',
+    'register_thanks' => '가입해 주셔서 감사합니다!',
+    'register_confirm' => 'ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\9c í\9b\84 ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ :appNameì\97\90 ì \91ê·¼í\95\98ì\84¸ì\9a\94.',
+    'registrations_disabled' => '가입할 수 없습니다.',
+    'registration_email_domain_invalid' => '이 메일 주소로는 이 사이트에 접근할 수 없습니다.',
+    'register_success' => '가입했습니다! 이제 로그인할 수 있습니다.',
 
 
     // Password Reset
-    'reset_password' => '암호 재설정',
-    'reset_password_send_instructions' => 'ë\8b¤ì\9d\8cì\97\90 ë©\94ì\9d¼ ì£¼ì\86\8c를 ì\9e\85ë ¥í\95\98ë©´ ë¹\84ë°\80ë²\88í\98¸ ì\9e¬ì\84¤ì \95 ë§\81í\81¬ê°\80 í\8f¬í\95¨ ë\90\9c ì\9d´ë©\94ì\9d¼ì\9d´ ì \84ì\86¡ë\90©니다.',
-    'reset_password_send_button' => '재설정 링크 보내기',
-    'reset_password_sent_success' => ':email로 재설정 링크를 보냈습니다.',
-    'reset_password_success' => '비밀번호가 재설정되었습니다.',
-    'email_reset_subject' => ':appName 암호를 재설정',
-    'email_reset_text' => '귀하의 계정에 대한 비밀번호 재설정 요청을 받았기 때문에 본 이메일이 발송되었습니다.',
-    'email_reset_not_requested' => 'ì\95\94í\98¸ ì\9e¬ì\84¤ì \95ì\9d\84 ì\9a\94ì²­í\95\98ì§\80 ì\95\8aì\9d\80 ê²½ì\9a° ë\8d\94 ì\9d´ì\83\81ì\9d\98 ì¡°ì¹\98ë\8a\94 í\95\84ì\9a\94í\95\98ì§\80 ì\95\8a습니다.',
+    'reset_password' => '비밀번호 바꾸기',
+    'reset_password_send_instructions' => 'ë©\94ì\9d¼ ì£¼ì\86\8c를 ì\9e\85ë ¥í\95\98ì\84¸ì\9a\94. ì\9d´ ì£¼ì\86\8cë¡\9c í\95´ë\8b¹ ê³¼ì \95ì\9d\84 ì\9c\84í\95\9c ë§\81í\81¬ë¥¼ ë³´ë\82¼ ê²\83ì\9e\85니다.',
+    'reset_password_send_button' => '메일 보내기',
+    'reset_password_sent_success' => ':email로 메일을 보냈습니다.',
+    'reset_password_success' => '비밀번호를 바꿨습니다.',
+    'email_reset_subject' => ':appName 비밀번호 바꾸기',
+    'email_reset_text' => '비밀번호를 바꿉니다.',
+    'email_reset_not_requested' => 'ì\9b\90í\95\98ì§\80 ì\95\8aë\8a\94ë\8b¤ë©´ ì\9d´ ê³¼ì \95ì\9d\80 í\95\84ì\9a\94 ì\97\86습니다.',
 
 
     // Email Confirmation
-    'email_confirm_subject' => ':appName의 이메일 주소 확인',
-    'email_confirm_greeting' => ':appName에 가입 ​​해 주셔서 감사합니다!',
-    'email_confirm_text' => 'ë\8b¤ì\9d\8c ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ ì\9d´ë©\94ì\9d¼ ì£¼ì\86\8c를 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤',
-    'email_confirm_action' => '이메일 주소를 확인',
-    'email_confirm_send_error' => 'Eë©\94ì\9d¼ í\99\95ì\9d¸ì\9d´ í\95\84ì\9a\94í\95\98ì§\80ë§\8c ì\8b\9cì\8a¤í\85\9cì\97\90ì\84\9c ë©\94ì\9d¼ì\9d\84 ë³´ë\82¼ ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. ê´\80리ì\9e\90ì\97\90ê²\8c ë¬¸ì\9d\98í\95\98ì\97¬ ë©\94ì\9d¼ì\9d´ ì \9cë\8c\80ë¡\9c ì\84¤ì \95ë\90\98ì\96´ ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'email_confirm_success' => '메일 주소가 확인되었습니다.',
-    'email_confirm_resent' => '확인 메일을 다시 보냈습니다. 받은 편지함을 확인하십시오.',
+    'email_confirm_subject' => ':appName 메일 인증',
+    'email_confirm_greeting' => ':appName로 가입해 주셔서 감사합니다!',
+    'email_confirm_text' => 'ë\8b¤ì\9d\8c ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ ì\9d¸ì¦\9dí\95\98ì\84¸ì\9a\94:',
+    'email_confirm_action' => '메일 인증',
+    'email_confirm_send_error' => 'ë©\94ì\9d¼ì\9d\84 ë³´ë\82¼ ì\88\98 ì\97\86ì\97\88ì\8aµë\8b\88ë\8b¤.',
+    'email_confirm_success' => '인증했습니다!',
+    'email_confirm_resent' => '다시 보냈습니다. 메일함을 확인하세요.',
 
-    'email_not_confirmed' => '메일 주소가 확인되지 않습니다',
-    'email_not_confirmed_text' => '메일 주소 확인이 완료되지 않습니다.',
-    'email_not_confirmed_click_link' => 'ë\93±ë¡\9dì\8b\9c ë°\9bì\9d\80 ì\9d´ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ê³  í\99\95ì\9d¸ ë§\81í\81¬ë¥¼ í\81´ë¦­í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'email_not_confirmed_resend' => '메일이 없으면 아래 양식을 통해 다시 제출하십시오.',
-    'email_not_confirmed_resend_button' => '확인 메일을 다시 전송',
+    'email_not_confirmed' => '인증하지 않았습니다.',
+    'email_not_confirmed_text' => '인증을 완료하지 않았습니다.',
+    'email_not_confirmed_click_link' => 'ë©\94ì\9d¼ì\9d\84 í\99\95ì\9d¸í\95\98ê³  ì\9d¸ì¦\9d ë§\81í\81¬ë¥¼ í\81´ë¦­í\95\98ì\84¸ì\9a\94.',
+    'email_not_confirmed_resend' => '메일 주소가 없다면 다음을 입력하세요.',
+    'email_not_confirmed_resend_button' => '다시 보내기',
 
     // User Invite
-    'user_invite_email_subject' => 'You have been invited to join :appName!',
-    'user_invite_email_greeting' => 'An account has been created for you on :appName.',
-    'user_invite_email_text' => 'Click the button below to set an account password and gain access:',
-    'user_invite_email_action' => 'Set Account Password',
-    'user_invite_page_welcome' => 'Welcome to :appName!',
-    'user_invite_page_text' => 'To finalise your account and gain access you need to set a password which will be used to log-in to :appName on future visits.',
-    'user_invite_page_confirm_button' => 'Confirm Password',
-    'user_invite_success' => 'Password set, you now have access to :appName!'
-];
\ No newline at end of file
+    'user_invite_email_subject' => ':appName에서 권유를 받았습니다.',
+    'user_invite_email_greeting' => ':appName에서 가입한 기록이 있습니다.',
+    'user_invite_email_text' => '다음 버튼을 눌러 확인하세요:',
+    'user_invite_email_action' => '비밀번호 설정',
+    'user_invite_page_welcome' => ':appName로 접속했습니다.',
+    'user_invite_page_text' => ':appName에 로그인할 때 입력할 비밀번호를 설정하세요.',
+    'user_invite_page_confirm_button' => '비밀번호 확인',
+    'user_invite_success' => '이제 :appName에 접근할 수 있습니다.'
+];
index 31d3e50241934331925ffcb985bf8639842f36a7..8d3a148a5f50215a900dd3af903b8ccf1b9ef1a6 100644 (file)
@@ -9,68 +9,68 @@ return [
     'confirm' => '확인',
     'back' => '뒤로',
     'save' => '저장',
-    'continue' => '계속하기',
+    'continue' => '계속',
     'select' => '선택',
-    'toggle_all' => 'Toggle All',
-    'more' => '더보기',
+    'toggle_all' => '모두 보기',
+    'more' => '더 보기',
 
     // Form Labels
     'name' => '이름',
     'description' => '설명',
-    'role' => '역할',
+    'role' => '권한',
     'cover_image' => '대표 이미지',
-    'cover_image_description' => '이 이미지는 약 440x250px 정도의 크기여야 합니다.',
+    'cover_image_description' => '이미지 규격은 440x250px 내외입니다.',
     
     // Actions
-    'actions' => 'Actions',
-    'view' => 'ë·°',
-    'view_all' => 'View All',
-    'create' => '생성',
-    'update' => '업데이트',
+    'actions' => '활동',
+    'view' => '보기',
+    'view_all' => '모두 보기',
+    'create' => '만들기',
+    'update' => '바꾸기',
     'edit' => '수정',
     'sort' => '정렬',
-    'move' => 'ì\9d´ë\8f\99',
+    'move' => 'ì\98®ê¸°ê¸°',
     'copy' => '복사',
-    'reply' => 'Reply',
-    'delete' => 'ì\82­ì \9c',
+    'reply' => '답글',
+    'delete' => 'ì§\80ì\9a°ê¸°',
     'search' => '검색',
-    'search_clear' => 'ê²\80ì\83\89기ë¡\9d ì\82­ì \9c',
-    'reset' => '초기화',
+    'search_clear' => '기ë¡\9d ì§\80ì\9a°ê¸°',
+    'reset' => '리셋',
     'remove' => '제거',
     'add' => '추가',
 
     // Sort Options
-    'sort_options' => 'Sort Options',
-    'sort_direction_toggle' => 'Sort Direction Toggle',
-    'sort_ascending' => 'Sort Ascending',
-    'sort_descending' => 'Sort Descending',
-    'sort_name' => 'Name',
-    'sort_created_at' => 'Created Date',
-    'sort_updated_at' => 'Updated Date',
+    'sort_options' => '정렬 기준',
+    'sort_direction_toggle' => '순서 반전',
+    'sort_ascending' => '오름차 순서',
+    'sort_descending' => '내림차 순서',
+    'sort_name' => '제목',
+    'sort_created_at' => '만든 날짜',
+    'sort_updated_at' => '수정한 날짜',
 
     // Misc
-    'deleted_user' => '삭제ë\90\9c ì\82¬ì\9a©ì\9e\90',
-    'no_activity' => '활동내역이 없음',
-    'no_items' => '사용가능한 항목이 없음',
-    'back_to_top' => '맨위로',
-    'toggle_details' => '상세 토글',
-    'toggle_thumbnails' => 'ì\8d¸ë\82´ì\9d¼ í\86 ê¸\80',
-    'details' => 'ì\83\81ì\84¸',
-    'grid_view' => '그리ë\93\9c ë·°',
-    'list_view' => '리ì\8a¤í\8a¸ë·°',
-    'default' => '기본설정',
-    'breadcrumb' => 'Breadcrumb',
+    'deleted_user' => '삭제í\95\9c ì\82¬ì\9a©ì\9e\90',
+    'no_activity' => '활동 없음',
+    'no_items' => '항목 없음',
+    'back_to_top' => '맨 위로',
+    'toggle_details' => '내용 보기',
+    'toggle_thumbnails' => 'ì\84¬ë\84¤ì\9d¼ ë³´ê¸°',
+    'details' => 'ì \95ë³´',
+    'grid_view' => '격ì\9e\90 ë³´ê¸°',
+    'list_view' => '목ë¡\9d ë³´ê¸°',
+    'default' => '기본 설정',
+    'breadcrumb' => '탐색 경로',
 
     // Header
-    'profile_menu' => 'Profile Menu',
-    'view_profile' => 'í\94\84ë¡\9cí\8c\8cì\9d¼ 보기',
-    'edit_profile' => 'í\94\84ë¡\9cí\8c\8cì\9d¼ ì\88\98ì \95í\95\98기',
+    'profile_menu' => '프로필',
+    'view_profile' => 'í\94\84ë¡\9cí\95\84 보기',
+    'edit_profile' => 'í\94\84ë¡\9cí\95\84 ë°\94꾸기',
 
     // Layout tabs
-    'tab_info' => 'Info',
-    'tab_content' => 'Content',
+    'tab_info' => '정보',
+    'tab_content' => '내용',
 
     // Email Content
-    'email_action_help' => '":actionText"버튼을 클릭하는 데 문제가 있으면 아래 URL을 복사하여 웹 브라우저에 붙여 넣으십시오:',
+    'email_action_help' => ':actionText를 클릭할 수 없을 때는 웹 브라우저에서 다음 링크로 접속할 수 있습니다.',
     'email_rights' => 'All rights reserved',
 ];
index fd7839e2f2c38161e35d68982541bfab4a0d742c..397d0d1879c12168090f7129c21b4b761755fd27 100644 (file)
@@ -6,28 +6,28 @@ return [
 
     // Image Manager
     'image_select' => '이미지 선택',
-    'image_all' => '전체',
-    'image_all_title' => '모든 이미지 보기',
-    'image_book_title' => 'ì\9d´ ì±\85ì\97\90 ì\97\85ë¡\9cë\93\9cë\90\9c ì\9d´ë¯¸ì§\80 ë³´ê¸°',
-    'image_page_title' => '이 페이지에 업로드된 이미지 보기',
-    'image_search_hint' => '이미지 이름으로 검색',
-    'image_uploaded' => ':uploadedDate에 업로드됨',
-    'image_load_more' => 'ë\8d\94 ë\88ë\9f¬ì\98¤기',
+    'image_all' => '모든 이미지',
+    'image_all_title' => '모든 이미지',
+    'image_book_title' => 'ì\9d´ ì±\85ì\9e\90ì\97\90ì\84\9c ì\93°ê³  ì\9e\88ë\8a\94 ì\9d´ë¯¸ì§\80',
+    'image_page_title' => '이 문서에서 쓰고 있는 이미지',
+    'image_search_hint' => '이미지 이름 검색',
+    'image_uploaded' => '올림 :uploadedDate',
+    'image_load_more' => 'ë\8d\94 ë¡\9cë\93\9cí\95\98기',
     'image_image_name' => '이미지 이름',
-    'image_delete_used' => '이 이미지는 다음 페이지에서 이용되고 있습니다.',
-    'image_delete_confirm' => 'ì\82­ì \9cí\95´ë\8f\84 ê´\9cì°®ì\9c¼ì\8b\9cë©´ ë\8b¤ì\8b\9c ì\82­ì \9c ë²\84í\8a¼ì\9d\84 ë\88\8cë\9f¬ì£¼ì\84¸ì\9a\94.',
-    'image_select_image' => '선택',
-    'image_dropzone' => 'ì\97\85ë¡\9cë\93\9c를 ì\9c\84í\95´ ì\9d´ë¯¸ì§\80를 ê°\80ì ¸ì\99\80 ë\86\93ê±°ë\82\98 í\81´ë¦­í\95\98ì\84¸ì\9a\94',
-    'images_deleted' => '이미지 삭제',
-    'image_preview' => '이미지 미리보기',
-    'image_upload_success' => 'ì\9d´ë¯¸ì§\80 ì\97\85ë¡\9cë\93\9cê°\80 ì\99\84ë£\8cë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'image_update_success' => 'ì\9d´ë¯¸ì§\80 ì\83\81ì\84¸ì \95ë³´ê°\80 ì\97\85ë\8d°ì\9d´í\8a¸ ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'image_delete_success' => '이미지가 삭제되었습니다.',
-    'image_upload_remove' => 'ì \9cê±°',
+    'image_delete_used' => '이 이미지는 다음 문서들이 쓰고 있습니다.',
+    'image_delete_confirm' => 'ì\9d´ ì\9d´ë¯¸ì§\80를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'image_select_image' => 'ì\9d´ë¯¸ì§\80 ì\84 í\83\9d',
+    'image_dropzone' => 'ì\97¬ê¸°ì\97\90 ì\9d´ë¯¸ì§\80를 ë\93\9cë¡­í\95\98ê±°ë\82\98 ì\97¬ê¸°ë¥¼ í\81´ë¦­í\95\98ì\84¸ì\9a\94. ì\9d´ë¯¸ì§\80를 ì\98¬ë¦´ ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤.',
+    'images_deleted' => '이미지 삭제',
+    'image_preview' => '이미지 미리 보기',
+    'image_upload_success' => 'ì\9d´ë¯¸ì§\80 ì\98¬ë¦¼',
+    'image_update_success' => 'ì\9d´ë¯¸ì§\80 ì \95ë³´ ì\88\98ì \95í\95¨',
+    'image_delete_success' => '이미지 삭제함',
+    'image_upload_remove' => 'ì\82­ì \9c',
 
     // Code Editor
     'code_editor' => '코드 수정',
-    'code_language' => 'ì½\94ë\93\9c ì\96¸ì\96´',
-    'code_content' => '코드 내용',
-    'code_save' => 'ì½\94ë\93\9c ì \80ì\9e¥',
+    'code_language' => '언어',
+    'code_content' => '내용',
+    'code_save' => '저장',
 ];
index 4e2692bc57c00ae1b6a504ba9fe642a15ff813f4..4d5e1bc83e2327ca66fdabedf3a97cc1ddc22092 100644 (file)
 return [
 
     // Shared
-    'recently_created' => 'ìµ\9cê·¼ì\9e\91ì\84±',
-    'recently_created_pages' => '최근 작성된 페이지',
-    'recently_updated_pages' => '최근 업데이트된 페이지',
-    'recently_created_chapters' => '최근 만들어진 챕터',
-    'recently_created_books' => '최근 만들어진 책',
-    'recently_created_shelves' => 'Recently Created Shelves',
-    'recently_update' => '최근 작성',
-    'recently_viewed' => '검색 기록',
-    'recent_activity' => '최근 활동',
-    'create_now' => '지금 만들기',
-    'revisions' => '변경이력',
-    'meta_revision' => '수정 #:revisionCount',
-    'meta_created' => '작성: :timeLength',
-    'meta_created_name' => '작성: :timeLength by :user',
-    'meta_updated' => 'ì\97\85ë\8d°ì\9d´í\8a¸ :timeLength',
-    'meta_updated_name' => 'ì\97\85ë\8d°ì\9d´í\8a¸ :timeLength by :user',
-    'entity_select' => '엔티티선택',
+    'recently_created' => 'ìµ\9cê·¼ì\97\90 ì\88\98ì \95í\95¨',
+    'recently_created_pages' => '최근에 만든 문서',
+    'recently_updated_pages' => '최근에 수정한 문서',
+    'recently_created_chapters' => '최근에 만든 챕터',
+    'recently_created_books' => '최근에 만든 책자',
+    'recently_created_shelves' => '최근에 만든 서가',
+    'recently_update' => '최근에 수정함',
+    'recently_viewed' => '최근에 읽음',
+    'recent_activity' => '최근에 활동함',
+    'create_now' => '바로 만들기',
+    'revisions' => '수정본',
+    'meta_revision' => '판본 #:revisionCount',
+    'meta_created' => '만듦 :timeLength',
+    'meta_created_name' => '만듦 :timeLength, :user',
+    'meta_updated' => 'ì\88\98ì \95í\95¨ :timeLength',
+    'meta_updated_name' => 'ì\88\98ì \95í\95¨ :timeLength, :user',
+    'entity_select' => '항목 선택',
     'images' => '이미지',
-    'my_recent_drafts' => '내 최근 초안',
-    'my_recently_viewed' => '검색 기록',
-    'no_pages_viewed' => '조회한 페이지가 없습니다.',
-    'no_pages_recently_created' => '최근 만들어진 페이지가 없습니다',
-    'no_pages_recently_updated' => '최근 업데이트된 페이지가없습니다',
-    'export' => '내보내기',
-    'export_html' => 'html 내보내기',
+    'my_recent_drafts' => '쓰다 만 문서',
+    'my_recently_viewed' => '내가 읽은 문서',
+    'no_pages_viewed' => '문서 없음',
+    'no_pages_recently_created' => '문서 없음',
+    'no_pages_recently_updated' => '문서 없음',
+    'export' => '파일로 받기',
+    'export_html' => 'Contained Web(.html) 파일',
     'export_pdf' => 'PDF 파일',
-    'export_text' => '일반 텍스트 파일',
+    'export_text' => 'Plain Text(.txt) 파일',
 
     // Permissions and restrictions
     'permissions' => '권한',
-    'permissions_intro' => 'ì\9d´ ì\84¤ì \95ì\9d\80 ê°\81 ì\82¬ì\9a©ì\9e\90ì\9d\98 ì\97­í\95 ë³´ë\8b¤ ì\9a°ì\84 í\95\98ì\97¬ ì \81ì\9a©ë\90©ë\8b\88ë\8b¤.',
-    'permissions_enable' => '커ì\8a¤í\85\80 ê¶\8cí\95\9c í\99\9cì\84±í\99\94',
+    'permissions_intro' => 'í\95\9cë²\88 í\97\88ì\9a©í\95\98ë©´ ì\9d´ ì\84¤ì \95ì\9d\80 ì\82¬ì\9a©ì\9e\90 ê¶\8cí\95\9cì\97\90 ì\9a°ì\84 í\95©ë\8b\88ë\8b¤.',
+    'permissions_enable' => 'ì\84¤ì \95 í\97\88ì\9a©',
     'permissions_save' => '권한 저장',
 
     // Search
     'search_results' => '검색 결과',
-    'search_total_results_found' => ':count 개의 결과를 찾았습니다.|총 :count 개의 결과를 찾았습니다.',
-    'search_clear' => 'ê²\80ì\83\89기ë¡\9d ì´\88기í\99\94',
-    'search_no_pages' => 'ê²\80ì\83\89ê²°ê³¼ê°\80 ì\97\86ì\8aµë\8b\88ë\8b¤.',
-    'search_for_term' => ':term 을(를) 검색합니다.',
-    'search_more' => '결과 더보기',
-    'search_filters' => 'ê²\80ì\83\89 í\95\84í\84°',
-    'search_content_type' => '컨텐츠 타입',
-    'search_exact_matches' => '정확히 일치합니다.',
-    'search_tags' => '테그 검색',
-    'search_options' => 'ì\84¤ì \95',
-    'search_viewed_by_me' => '내가본것',
-    'search_not_viewed_by_me' => '내가안본것',
-    'search_permissions_set' => '권한 설정',
+    'search_total_results_found' => ':count개|총 :count개',
+    'search_clear' => '기ë¡\9d ì§\80ì\9a°ê¸°',
+    'search_no_pages' => 'ê²°ê³¼ ì\97\86ì\9d\8c',
+    'search_for_term' => ':term 검색',
+    'search_more' => '더 많은 결과',
+    'search_filters' => 'ê³ ê¸\89 ê²\80ì\83\89',
+    'search_content_type' => '형식',
+    'search_exact_matches' => '정확히 일치',
+    'search_tags' => '꼬리표 일치',
+    'search_options' => 'ì\84 í\83\9d',
+    'search_viewed_by_me' => '내가 읽음',
+    'search_not_viewed_by_me' => '내가 읽지 않음',
+    'search_permissions_set' => '권한 설정',
     'search_created_by_me' => '내가 만듦',
-    'search_updated_by_me' => 'ë\82´ê°\80 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_date_options' => '날짜 설정',
-    'search_updated_before' => 'ì\9d´ì \84ì\97\90 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_updated_after' => 'ì\9d´í\9b\84ì\97\90 ì\97\85ë\8d°ì\9d´í\8a¸함',
-    'search_created_before' => '이전에 생성함',
-    'search_created_after' => '이후에 생성함',
+    'search_updated_by_me' => 'ë\82´ê°\80 ì\88\98ì \95함',
+    'search_date_options' => '날짜',
+    'search_updated_before' => 'ì\9d´ì \84ì\97\90 ì\88\98ì \95함',
+    'search_updated_after' => 'ì\9d´í\9b\84ì\97\90 ì\88\98ì \95함',
+    'search_created_before' => '이전에 만듦',
+    'search_created_after' => '이후에 만듦',
     'search_set_date' => '날짜 설정',
-    'search_update' => '검색 업데이트',
+    'search_update' => '검색',
 
     // Shelves
-    'shelf' => 'ì±\85ê½\83ì\9d´',
-    'shelves' => 'ì±\85ê½\83ì\9d´',
-    'x_shelves' => ':count Shelf|:count Shelves',
-    'shelves_long' => 'ì±\85ê½\83ì\9d´',
-    'shelves_empty' => '책꽃이가 만들어지지 않았습니다.',
-    'shelves_create' => 'ì\83\88ì±\85ê½\83ì\9d´ 만들기',
-    'shelves_popular' => '인기있는 책꽃이',
-    'shelves_new' => 'ì\83\88ë¡\9cì\9a´ ì±\85ê½\83ì\9d´',
-    'shelves_new_action' => 'New Shelf',
-    'shelves_popular_empty' => '인기있는 책꽃이가 여기에 나타납니다.',
-    'shelves_new_empty' => '가장 최근에 만들어진 책꽃이가 여기에 나타납니다.',
-    'shelves_save' => 'ì±\85ê½\83ì\9d´ ì \80ì\9e¥',
-    'shelves_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì\9e\88ë\8a\94 ì±\85',
-    'shelves_add_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì±\85ì\9d\84 ì¶\94ê°\80í\95©ë\8b\88ë\8b¤',
-    'shelves_drag_books' => 'ì\9d´ ì±\85ê½\83ì\9d´ì\97\90 ì±\85ì\9d\84 ì¶\94ê°\80í\95\98기 ì\9c\84í\95´ ë\81\8cì\96´ë\86\93ì\9c¼세요.',
-    'shelves_empty_contents' => '이책꽃이엔 등록된 책이 없습니다.',
-    'shelves_edit_and_assign' => 'ì±\85ì\9d\84 ë\93±ë¡\9dí\95\98기 ì\9c\84í\95´ ì±\85ê½\83ì\9d´ë¥¼ ì\88\98ì \95',
-    'shelves_edit_named' => ':name 책꽃이 수정',
-    'shelves_edit' => 'ì±\85ê½\83ì\9d´ ì\88\98ì \95',
-    'shelves_delete' => 'ì±\85ê½\83ì\9d´ ì\82­ì \9c',
-    'shelves_delete_named' => ':name ì±\85ê½\83ì\9d´ë¥¼ ì\82­ì \9cí\95©ë\8b\88ë\8b¤.',
-    'shelves_delete_explain' => "':name' 이름의 책꽃이가 삭제됩니다. 포함된 책은 삭제되지 않습니다.",
-    'shelves_delete_confirmation' => 'ì\9d´ ì±\85ê½\83ì\9d´ë¥¼ ì\82­ì \9cí\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'shelves_permissions' => 'ì±\85ê½\83ì\9d´ 권한',
-    'shelves_permissions_updated' => 'ì±\85ê½\83ì\9d´ ê¶\8cí\95\9cì\9d´ ì\97\85ë\8d°ì\9d´í\8a¸ ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'shelves_permissions_active' => 'ì±\85ê½\83ì\9d´ ê¶\8cí\95\9c í\99\9cì\84±í\99\94',
-    'shelves_copy_permissions_to_books' => '책에 권한을 복사합니다.',
-    'shelves_copy_permissions' => '권한 복사',
-    'shelves_copy_permissions_explain' => 'ì\9d´ ì±\85ê½\82ì\9d´ì\9d\98 í\98\84ì\9e¬ ê¶\8cí\95\9c ì\84¤ì \95ì\9d´ ì\95\88ì\97\90 í\8f¬í\95¨ ë\90\9c ëª¨ë\93  ì±\85ì\97\90 ì \81ì\9a©ë\90©ë\8b\88ë\8b¤. í\99\9cì\84±í\99\94í\95\98기 ì \84ì\97\90ì\9d´ ì±\85ê½\82ì\9d´ì\9d\98 ì\82¬ì\9a© ê¶\8cí\95\9cì\9d´ ë³\80ê²½ë\90\98ì\97\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'shelves_copy_permission_success' => '책꽃이의 권한이 :count 개의 책에 복사되었습니다.',
+    'shelf' => 'ì\84\9cê°\80',
+    'shelves' => 'ì\84\9cê°\80',
+    'x_shelves' => '서가 :count개|총 :count개',
+    'shelves_long' => 'ì\84\9cê°\80',
+    'shelves_empty' => '만든 서가가 없습니다.',
+    'shelves_create' => 'ì\84\9cê°\80 만들기',
+    'shelves_popular' => '많이 읽은 서가',
+    'shelves_new' => 'ì\83\88ë¡\9cì\9a´ ì\84\9cê°\80',
+    'shelves_new_action' => '새로운 서가',
+    'shelves_popular_empty' => '많이 읽은 서가 목록',
+    'shelves_new_empty' => '새로운 서가 목록',
+    'shelves_save' => '저장',
+    'shelves_books' => 'ì\9d´ ì\84\9cê°\80ì\97\90 ì\9e\88ë\8a\94 ì±\85ì\9e\90ë\93¤',
+    'shelves_add_books' => 'ì\9d´ ì\84\9cê°\80ì\97\90 ì±\85ì\9e\90 ì¶\94ê°\80',
+    'shelves_drag_books' => 'ì\97¬ê¸°ì\97\90 ì±\85ì\9e\90를 ë\93\9cë¡­í\95\98세요.',
+    'shelves_empty_contents' => '이 서가에 책자가 없습니다.',
+    'shelves_edit_and_assign' => 'ì\84\9cê°\80 ë°\94꾸기ë¡\9c ì±\85ì\9e\90를 ì¶\94ê°\80í\95\98ì\84¸ì\9a\94.',
+    'shelves_edit_named' => ':name 바꾸기',
+    'shelves_edit' => 'ì\84\9cê°\80 ë°\94꾸기',
+    'shelves_delete' => 'ì\84\9cê°\80 ì§\80ì\9a°ê¸°',
+    'shelves_delete_named' => ':name ì§\80ì\9a°ê¸°',
+    'shelves_delete_explain' => ':name을 지웁니다. 책자는 지우지 않습니다.',
+    'shelves_delete_confirmation' => 'ì\9d´ ì\84\9cê°\80를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'shelves_permissions' => 'ì\84\9cê°\80 권한',
+    'shelves_permissions_updated' => 'ì\84\9cê°\80 ê¶\8cí\95\9c ë°\94ê¿\88',
+    'shelves_permissions_active' => 'ì\84\9cê°\80 ê¶\8cí\95\9c í\97\88ì\9a©í\95¨',
+    'shelves_copy_permissions_to_books' => '권한 맞춤',
+    'shelves_copy_permissions' => '실행',
+    'shelves_copy_permissions_explain' => 'ì\84\9cê°\80ì\9d\98 ëª¨ë\93  ì±\85ì\9e\90ì\97\90 ì\9d´ ê¶\8cí\95\9cì\9d\84 ì \81ì\9a©í\95©ë\8b\88ë\8b¤. ì\84\9cê°\80ì\9d\98 ê¶\8cí\95\9cì\9d\84 ì \80ì\9e¥í\96\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\84¸ì\9a\94.',
+    'shelves_copy_permission_success' => '책자 :count개 권한 바꿈',
 
     // Books
-    'book' => 'ì±\85',
-    'books' => 'ì±\85ë\93¤',
-    'x_books' => ':count 책|:count 책들',
-    'books_empty' => '책이 만들어지지 않았습니다.',
-    'books_popular' => '인기있는 책',
-    'books_recent' => '최근 책',
-    'books_new' => '새로운 책',
-    'books_new_action' => 'New Book',
-    'books_popular_empty' => '가장 인기있는 책이 여기에 보입니다.',
-    'books_new_empty' => '가장 최근에 만든 책이 여기에 표시됩니다.',
-    'books_create' => 'ì\83\88ë¡\9cì\9a´ ì±\85 만들기',
-    'books_delete' => '책 삭제하기',
-    'books_delete_named' => ':bookName 책 삭제하기',
-    'books_delete_explain' => '\':bookName\' 이름의 책이 삭제됩니다. 모든 페이지와 챕터가 삭제됩니다.',
-    'books_delete_confirmation' => 'ì\9d´ ì±\85ì\9d\84 ì\82­ì \9c í\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'books_edit' => '책 수정',
-    'books_edit_named' => ':bookName 책 수정',
-    'books_form_book_name' => '책 이름',
-    'books_save' => 'ì±\85 ì \80ì\9e¥',
-    'books_permissions' => '책 권한',
-    'books_permissions_updated' => '책 권한이 업데이트 되었습니다.',
-    'books_empty_contents' => 'ì\9d´ ì±\85ì\97\90 ë\8c\80í\95\9c í\8e\98ì\9d´ì§\80 ë\98\90ë\8a\94 ì\9e¥ì\9d´ ì\9e\91ì\84±ë\90\98ì§\80 ì\95\8aì\95\98습니다.',
-    'books_empty_create_page' => '새로운 페이지 만들기',
-    'books_empty_sort_current_book' => '현제 책 정렬하기',
-    'books_empty_add_chapter' => '챕터 추가하기',
-    'books_permissions_active' => '책 권한 활성화',
-    'books_search_this' => '이책 찾기',
-    'books_navigation' => '책 네비게이션',
-    'books_sort' => '책 구성 정렬하기',
-    'books_sort_named' => ':bookName ì±\85 ì \95ë ¬í\95\98기',
-    'books_sort_name' => 'Sort by Name',
-    'books_sort_created' => 'Sort by Created Date',
-    'books_sort_updated' => 'Sort by Updated Date',
-    'books_sort_chapters_first' => 'Chapters First',
-    'books_sort_chapters_last' => 'Chapters Last',
-    'books_sort_show_other' => '다른책 보기',
-    'books_sort_save' => 'ì\83\88ë¡\9cì\9a´ ì\88\9cì\84\9c ì \80ì\9e¥',
+    'book' => 'ì\84\9cê³ ',
+    'books' => 'ì\84\9cê³ ',
+    'x_books' => '책자 :count개|총 :count개',
+    'books_empty' => '만든 책자가 없습니다.',
+    'books_popular' => '많이 읽은 책자',
+    'books_recent' => '최근에 읽은 책자',
+    'books_new' => '새로운 책',
+    'books_new_action' => '새로운 책자',
+    'books_popular_empty' => '많이 읽은 책자 목록',
+    'books_new_empty' => '새로운 책자 목록',
+    'books_create' => 'ì±\85ì\9e\90 만들기',
+    'books_delete' => '책자 지우기',
+    'books_delete_named' => ':bookName(을)를 지웁니다.',
+    'books_delete_explain' => ':bookName에 있는 모든 챕터와 문서도 지웁니다.',
+    'books_delete_confirmation' => 'ì\9d´ ì±\85ì\9e\90를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'books_edit' => '책자 바꾸기',
+    'books_edit_named' => ':bookName(을)를 바꿉니다.',
+    'books_form_book_name' => '책 이름',
+    'books_save' => '저장',
+    'books_permissions' => '책 권한',
+    'books_permissions_updated' => '권한 저장함',
+    'books_empty_contents' => 'ì\9d´ ì±\85ì\9e\90ì\97\90 ì±\95í\84°ë\82\98 ë¬¸ì\84\9cê°\80 ì\97\86습니다.',
+    'books_empty_create_page' => '문서 만들기',
+    'books_empty_sort_current_book' => '읽고 있는 책자 정렬',
+    'books_empty_add_chapter' => '챕터 만들기',
+    'books_permissions_active' => '책자 권한 허용함',
+    'books_search_this' => '이 책자에서 검색',
+    'books_navigation' => '목차',
+    'books_sort' => '다른 책자들',
+    'books_sort_named' => ':bookName ì \95ë ¬',
+    'books_sort_name' => '제목',
+    'books_sort_created' => '만든 날짜',
+    'books_sort_updated' => '수정한 날짜',
+    'books_sort_chapters_first' => '챕터 우선',
+    'books_sort_chapters_last' => '문서 우선',
+    'books_sort_show_other' => '다른 책자들',
+    'books_sort_save' => 'ì \81ì\9a©',
 
     // Chapters
     'chapter' => '챕터',
     'chapters' => '챕터',
-    'x_chapters' => ':count 개 챕터|:count 챔터들',
-    'chapters_popular' => '인기있는 챕터',
-    'chapters_new' => '새로운 챕',
-    'chapters_create' => 'ì\83\88ë¡\9cì\9a´ ì±\95í\84° ë§\8cë\93¤ê¸°',
-    'chapters_delete' => 'ì±\95í\84° ì\82­ì \9c',
-    'chapters_delete_named' => ':chapterName 챕터 지우기',
-    'chapters_delete_explain' => '\':chapterName\' 챕터를 지웁니다. 챕터에서 모든 페이지가 삭제되며 페이지가 상위 책에 추가됩니다.',
-    'chapters_delete_confirm' => 'ì \95ë§\90ë¡\9c ì±\95í\84°ë¥¼ ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ë\94°?',
-    'chapters_edit' => '챕터 수정',
-    'chapters_edit_named' => ':chapterName 챕터 수정',
-    'chapters_save' => 'ì±\95í\84° ì \80ì\9e¥',
-    'chapters_move' => 'ì±\95í\84° ì\9d´ë\8f\99',
-    'chapters_move_named' => ':chapterName ì±\95í\84° ì\9d´ë\8f\99',
-    'chapter_move_success' => ':bookName 으로 챕터를 이동하였습니다.',
+    'x_chapters' => '챕터 :count개|총 :count개',
+    'chapters_popular' => '많이 읽은 챕터',
+    'chapters_new' => '새로운 챕',
+    'chapters_create' => '챕터 만들기',
+    'chapters_delete' => 'ì±\95í\84° ì§\80ì\9a°ê¸°',
+    'chapters_delete_named' => ':chapterName(을)를 지웁니다.',
+    'chapters_delete_explain' => ':chapterName에 있는 모든 문서는 챕터에서 벗어날 뿐 지우지 않습니다.',
+    'chapters_delete_confirm' => 'ì\9d´ ì±\95í\84°ë¥¼ ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'chapters_edit' => '챕터 바꾸기',
+    'chapters_edit_named' => ':chapterName 바꾸기',
+    'chapters_save' => '저장',
+    'chapters_move' => 'ì±\95í\84° ì\98®ê¸°ê¸°',
+    'chapters_move_named' => ':chapterName ì\98®ê¸°ê¸°',
+    'chapter_move_success' => ':bookName(으)로 옮김',
     'chapters_permissions' => '챕터 권한',
-    'chapters_empty' => 'ì±\95í\84°ì\97\90 í\8f¬í\95¨ë\90\9c í\8e\98ì\9d´ì§\80가 없습니다.',
-    'chapters_permissions_active' => '챕터 권한 활동',
-    'chapters_permissions_success' => 'ì±\95í\84° ê¶\8cí\95\9c ì\88\98ì \95ë\90¨',
-    'chapters_search_this' => '이 챕터 찾기',
+    'chapters_empty' => 'ì\9d´ ì±\95í\84°ì\97\90 ë¬¸ì\84\9c가 없습니다.',
+    'chapters_permissions_active' => '문서 권한 허용함',
+    'chapters_permissions_success' => 'ê¶\8cí\95\9c ì \80ì\9e¥í\95¨',
+    'chapters_search_this' => '이 챕터에서 검색',
 
     // Pages
-    'page' => '페이지',
-    'pages' => '페이지들',
-    'x_pages' => ':count 개의 페이지|:count 개의 페이지들',
-    'pages_popular' => '인기있는 페이지',
-    'pages_new' => '새로운 페이지',
-    'pages_attachments' => '첨부파일',
-    'pages_navigation' => '페이지 네비게이션',
-    'pages_delete' => '페이지 지우기',
-    'pages_delete_named' => ':pageName 페이지 지우기',
-    'pages_delete_draft_named' => ':pageName ì´\88ì\95\88 í\8e\98ì\9d´ì§\80 ì§\80ì\9a°ê¸°',
-    'pages_delete_draft' => 'ì´\88ì\95\88 í\8e\98ì\9d´ì§\80 지우기',
-    'pages_delete_success' => '페이지 삭제됨',
-    'pages_delete_draft_success' => 'ì´\88ì\95\88í\8e\98ì\9d´ì§\80 ì\82­ì \9cë\90¨',
-    'pages_delete_confirm' => 'ì \95ë§\90ë¡\9c ì\9d´ í\8e\98ì\9d´ì§\80를 ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'pages_delete_draft_confirm' => 'ì \95ë§\90ë¡\9c ì´\88ì\95\88í\8e\98ì\9d´ì§\80를 ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'pages_editing_named' => ':pageName 페이지 수정',
-    'pages_edit_draft_options' => 'Draft Options',
-    'pages_edit_save_draft' => '초안 저장',
-    'pages_edit_draft' => '페이지 초안 수정',
-    'pages_editing_draft' => 'ì´\88ì\95\88 ì\88\98ì \95ì¤\91',
-    'pages_editing_page' => '페이지 수정중',
-    'pages_edit_draft_save_at' => '초안이 저장됨 ',
-    'pages_edit_delete_draft' => 'ì´\88ì\95\88 ì\82­ì \9c',
-    'pages_edit_discard_draft' => '초안 버리기',
-    'pages_edit_set_changelog' => '변경내역 남기기',
-    'pages_edit_enter_changelog_desc' => 'ì\96´ë\96¤ ë\82´ì\9a©ì\97\90 ë\8c\80í\95\98ì\97¬ ë³\80ê²½í\95\98ì\85¨ë\82\98ì\9a\94?',
-    'pages_edit_enter_changelog' => '변경내역 입력',
-    'pages_save' => '페이지 저장',
-    'pages_title' => '페이지 제목',
-    'pages_name' => '페이지 이름',
-    'pages_md_editor' => '편집자',
-    'pages_md_preview' => 'Preview',
-    'pages_md_insert_image' => 'ì\9d´ë¯¸ì§\80 ì\82½ì\9e\85',
-    'pages_md_insert_link' => '전체링크 입력',
-    'pages_md_insert_drawing' => '드로잉 넣기',
-    'pages_not_in_chapter' => '페이지가 챕터에 있지않습니다.',
-    'pages_move' => '페이지 옮기기',
-    'pages_move_success' => '":parentName"로 페이지를 이동하였습니다.',
-    'pages_copy' => '페이지 복사',
-    'pages_copy_desination' => '경로(desination) 복사',
-    'pages_copy_success' => '페이지가 성공적으로 복사되었습니다',
-    'pages_permissions' => '페이지 권한',
-    'pages_permissions_success' => '페이지 권한이 업데이트 되었습니다.',
-    'pages_revision' => '변경이력',
-    'pages_revisions' => '페이지 변경이력',
-    'pages_revisions_named' => ':pageName페이지의 변경이력내역',
-    'pages_revision_named' => ':pageName페이지의 변경이력',
-    'pages_revisions_created_by' => 'Created By',
-    'pages_revisions_date' => '변경일',
-    'pages_revisions_number' => '#',
-    'pages_revisions_numbered' => 'Revision #:id',
-    'pages_revisions_numbered_changes' => 'Revision #:id Changes',
-    'pages_revisions_changelog' => '변경내역',
-    'pages_revisions_changes' => 'ë³\80ê²½ì\82¬í\95­ ë³´ê¸°',
-    'pages_revisions_current' => '현재 버전',
-    'pages_revisions_preview' => '미리보기',
-    'pages_revisions_restore' => 'ë\90\98ë\8f\8c리기',
-    'pages_revisions_none' => 'ì\9d´ í\8e\98ì\9d´ì§\80ì\97\90ë\8a\94 ë³\80ê²½ì\9d´ë ¥이 없습니다.',
-    'pages_copy_link' => '링크복사',
-    'pages_edit_content_link' => '링크 수정',
-    'pages_permissions_active' => '페이지 권한 활성화',
-    'pages_initial_revision' => 'ìµ\9cì´\88 ì\9e\91ì\84±',
-    'pages_initial_name' => 'ì\83\88 í\8e\98ì\9d´ì§\80',
-    'pages_editing_draft_notification' => ':timeDiff 전에 저장된 초안을 최근 편집하셨습니다.',
-    'pages_draft_edited_notification' => 'ì\9d´ í\8e\98ì\9d´ì§\80ë\8a\94 ê·¸ ì\9d´í\9b\84ë¡\9c ì\97\85ë\8d°ì\9d´í\8a¸ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤. ì\9d´ ì´\88ì\95\88ì\9d\84 í\8f\90기í\95\98ë\8a\94 ê²\83이 좋습니다.',
+    'page' => '문서',
+    'pages' => '문서',
+    'x_pages' => '문서 :count개|총 :count개',
+    'pages_popular' => '많이 읽은 문서',
+    'pages_new' => '새로운 문서',
+    'pages_attachments' => '첨부',
+    'pages_navigation' => '목차',
+    'pages_delete' => '문서 지우기',
+    'pages_delete_named' => ':pageName 지우기',
+    'pages_delete_draft_named' => ':pageName 지우기',
+    'pages_delete_draft' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c 지우기',
+    'pages_delete_success' => '문서 지움',
+    'pages_delete_draft_success' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c ì§\80ì\9b\80',
+    'pages_delete_confirm' => 'ì\9d´ ë¬¸ì\84\9c를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'pages_delete_draft_confirm' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'pages_editing_named' => ':pageName 수정',
+    'pages_edit_draft_options' => '쓰다 만 문서 선택',
+    'pages_edit_save_draft' => '보관',
+    'pages_edit_draft' => '쓰다 만 문서 수정',
+    'pages_editing_draft' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c ì\88\98ì \95',
+    'pages_editing_page' => '문서 수정',
+    'pages_edit_draft_save_at' => '보관함: ',
+    'pages_edit_delete_draft' => '삭제',
+    'pages_edit_discard_draft' => '기',
+    'pages_edit_set_changelog' => '수정본 설명',
+    'pages_edit_enter_changelog_desc' => 'ì\88\98ì \95본 ì\84¤ëª\85',
+    'pages_edit_enter_changelog' => '설명',
+    'pages_save' => '저장',
+    'pages_title' => '문서 제목',
+    'pages_name' => '문서 이름',
+    'pages_md_editor' => '에디터',
+    'pages_md_preview' => '미리 보기',
+    'pages_md_insert_image' => 'ì\9d´ë¯¸ì§\80 ì\94ê°\80',
+    'pages_md_insert_link' => '내부 링크',
+    'pages_md_insert_drawing' => '드로잉 추가',
+    'pages_not_in_chapter' => '챕터에 있는 문서가 아닙니다.',
+    'pages_move' => '문서 옮기기',
+    'pages_move_success' => ':parentName(으)로 옮김',
+    'pages_copy' => '문서 복제',
+    'pages_copy_desination' => '복제할 위치',
+    'pages_copy_success' => '복제함',
+    'pages_permissions' => '문서 권한',
+    'pages_permissions_success' => '문서 권한 바꿈',
+    'pages_revision' => '수정본',
+    'pages_revisions' => '문서 수정본',
+    'pages_revisions_named' => ':pageName 수정본',
+    'pages_revision_named' => ':pageName 수정본',
+    'pages_revisions_created_by' => '만든 사용자',
+    'pages_revisions_date' => '수정한 날짜',
+    'pages_revisions_number' => 'No.',
+    'pages_revisions_numbered' => '수정본 :id',
+    'pages_revisions_numbered_changes' => '수정본 :id에서 바꾼 부분',
+    'pages_revisions_changelog' => '설명',
+    'pages_revisions_changes' => 'ë°\94ê¾¼ ë¶\80ë¶\84',
+    'pages_revisions_current' => '현재 판본',
+    'pages_revisions_preview' => '미리 보기',
+    'pages_revisions_restore' => 'ë³µì\9b\90',
+    'pages_revisions_none' => 'ì\88\98ì \95본이 없습니다.',
+    'pages_copy_link' => '주소 복사',
+    'pages_edit_content_link' => '수정',
+    'pages_permissions_active' => '문서 권한 허용함',
+    'pages_initial_revision' => 'ì²\98ì\9d\8c í\8c\90본',
+    'pages_initial_name' => 'ì \9c목 ì\97\86ì\9d\8c',
+    'pages_editing_draft_notification' => ':timeDiff에 쓰다 만 문서입니다.',
+    'pages_draft_edited_notification' => 'ìµ\9cê·¼ì\97\90 ì\88\98ì \95í\95\9c ë¬¸ì\84\9cì\9d´ê¸° ë\95\8c문ì\97\90 ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c를 í\8f\90기í\95\98ë\8a\94 í\8e¸이 좋습니다.',
     'pages_draft_edit_active' => [
-        'start_a' => ':countëª\85ì\9d\98 ì\82¬ì\9a©ì\9e\90ê°\80 ì\9d´ í\8e\98ì\9d´ì§\80를 ì\88\98ì \95ì¤\91ì\9e\85니다.',
-        'start_b' => ':userName가(이) 페이지를 수정중입니다.',
-        'time_a' => '페이지가 마지막으로 업데이트 된 이후',
-        'time_b' => '지난 :minCount분 동안',
-        'message' => ':start :time. 서로의 업데이트를 덮어 쓰지 않도록 주의하십시오!',
+        'start_a' => ':countëª\85ì\9d´ ì\9d´ ë¬¸ì\84\9c를 ì\88\98ì \95í\95\98ê³  ì\9e\88ì\8aµ니다.',
+        'start_b' => ':userName이 이 문서를 수정하고 있습니다.',
+        'time_a' => '수정본이 생겼습니다.',
+        'time_b' => '(:minCount분 전)',
+        'message' => ':start :time. 다른 사용자의 수정본을 덮어쓰지 않도록 주의하세요.',
     ],
-    'pages_draft_discarded' => 'ì´\88ì\95\88ì\9d´ ì\82­ì \9cë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤. í\8e¸ì§\91기ê°\80 í\98\84ì\9e¬ í\8e\98ì\9d´ì§\80 ì\9e\91ì\84±ì\9e\90ë¡\9c ì\97\85ë\8d°ì\9d´í\8a¸ë\90\98ì\97\88ì\8aµ니다.',
-    'pages_specific' => '특정 페이지',
-    'pages_is_template' => 'Page Template',
+    'pages_draft_discarded' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c를 ì§\80ì\9b ì\8aµë\8b\88ë\8b¤. ì\97\90ë\94\94í\84°ì\97\90 í\98\84ì\9e¬ í\8c\90본ì\9d´ ë\82\98í\83\80ë\82©니다.',
+    'pages_specific' => '특정한 문서',
+    'pages_is_template' => '템플릿',
 
     // Editor Sidebar
-    'page_tags' => '페이지 테그',
-    'chapter_tags' => '챕터 테그',
-    'book_tags' => '책 테그',
-    'shelf_tags' => 'ì±\85ê½\83ì\9d´ í\85\8cê·¸',
-    'tag' => '테그',
-    'tags' =>  '테그들',
-    'tag_name' =>  'Tag Name',
-    'tag_value' => '테그 값 (선택사항)',
-    'tags_explain' => "컨텐츠를 더 잘 분류하기 위해 테그를 추가하세요! \n 보다 상세한 구성을 위해 태그값을 할당 할 수 있습니다.",
-    'tags_add' => '다른 테그 추가',
-    'tags_remove' => 'Remove this tag',
-    'attachments' => '첨부',
-    'attachments_explain' => 'ì\9d¼ë¶\80 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ê±°ë\82\98 í\8e\98ì\9d´ì§\80ì\97\90 í\91\9cì\8b\9c í\95  ë§\81í\81¬ë¥¼ ì²¨ë¶\80í\95\98ì\8b­ì\8b\9cì\98¤. í\8e\98ì\9d´ì§\80 ì\82¬ì\9d´ë\93\9c ë°\94ì\97\90 í\91\9cì\8b\9cë\90©ë\8b\88ë\8b¤.',
-    'attachments_explain_instant_save' => 'ë³\80ê²½ ì\82¬í\95­ì\9d\80 ì¦\89ì\8b\9c ì \80ì\9e¥ë\90©ë\8b\88ë\8b¤.',
-    'attachments_items' => '첨부된 항목',
-    'attachments_upload' => '차일 업로드',
-    'attachments_link' => '링크 첨부',
+    'page_tags' => '문서 꼬리표',
+    'chapter_tags' => '챕터 꼬리표',
+    'book_tags' => '책자 꼬리표',
+    'shelf_tags' => 'ì\84\9cê°\80 ê¼¬ë¦¬í\91\9c',
+    'tag' => '꼬리표',
+    'tags' =>  '꼬리표',
+    'tag_name' =>  '꼬리표 이름',
+    'tag_value' => '리스트 값 (선택 사항)',
+    'tags_explain' => '태그로 문서를 분류하세요.',
+    'tags_add' => '그 추가',
+    'tags_remove' => '태그 삭제',
+    'attachments' => '첨부 파일',
+    'attachments_explain' => 'í\8c\8cì\9d¼ì\9d´ë\82\98 ë§\81í\81¬ë¥¼ ì²¨ë¶\80í\95\98ì\84¸ì\9a\94. ì \95ë³´ í\83­ì\97\90 ë\82\98í\83\80ë\82©ë\8b\88ë\8b¤.',
+    'attachments_explain_instant_save' => 'ì\97¬ê¸°ì\97\90ì\84\9c ë°\94ê¾¼ ë\82´ì\9a©ì\9d\80 ë°\94ë¡\9c ì \81ì\9a©í\95©ë\8b\88ë\8b¤.',
+    'attachments_items' => '첨부한 파일들',
+    'attachments_upload' => '파일 올리기',
+    'attachments_link' => '링크 첨부',
     'attachments_set_link' => '링크 설정',
-    'attachments_delete_confirm' => '삭제를 다시 클릭하면 첨부파일이 완전히 삭제됩니다.',
-    'attachments_dropzone' => '파일 놓기 또는 여기를 클릭하여 파일 첨부',
-    'attachments_no_files' => 'ì\97\85ë¡\9cë\93\9c ë\90\9c í\8c\8cì\9d¼ì\9d´ ì\97\86ì\8aµë\8b\88ë\8b¤.',
-    'attachments_explain_link' => 'í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ì§\80 ì\95\8aì\9c¼ë ¤ë\8a\94 ê²½ì\9a° ë§\81í\81¬ë¥¼ ì²¨ë¶\80 í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\9d´ ë§\81í\81¬ë\8a\94 ë\8b¤ë¥¸ í\8e\98ì\9d´ì§\80ì\97\90 ë\8c\80í\95\9c ë§\81í\81¬ì\9d´ê±°ë\82\98 í\81´ë\9d¼ì\9a°ë\93\9cì\97\90ì\9e\88ë\8a\94 í\8c\8cì\9d¼ì\97\90 ë\8c\80í\95\9c ë§\81í\81¬ ì\9d¼ 수 있습니다.',
+    'attachments_delete_confirm' => '삭제하려면 버튼을 한 번 더 클릭하세요.',
+    'attachments_dropzone' => '여기에 파일을 드롭하거나 여기를 클릭하세요.',
+    'attachments_no_files' => 'ì\98¬ë¦° í\8c\8cì\9d¼ ì\97\86ì\9d\8c',
+    'attachments_explain_link' => 'í\8c\8cì\9d¼ì\9d\84 ì\98¬ë¦¬ì§\80 ì\95\8aê³  ë§\81í\81¬ë¡\9c ì²¨ë¶\80í\95  수 있습니다.',
     'attachments_link_name' => '링크 이름',
-    'attachment_link' => '첨부 링크',
-    'attachments_link_url' => '파일로 첨부',
-    'attachments_link_url_hint' => 'Url, 사이트 또는 파일',
-    'attach' => '첨부',
+    'attachment_link' => '파일 주소',
+    'attachments_link_url' => '파일로 링크',
+    'attachments_link_url_hint' => '파일 주소',
+    'attach' => '파일 첨부',
     'attachments_edit_file' => '파일 수정',
-    'attachments_edit_file_name' => '파일이름',
-    'attachments_edit_drop_upload' => '파일을 놓거나 여기를 클릭하여 업로드 및 덮어 쓰기',
-    'attachments_order_updated' => '첨부 순서 업데이트',
-    'attachments_updated_success' => '첨부파일 상세내용 업데이트 성공',
-    'attachments_deleted' => '첨부파일 삭제',
-    'attachments_file_uploaded' => '파일이 성공적으로 업로드 되었습니다.',
-    'attachments_file_updated' => '파일이 성공적으로 업데이트 되었습니다.',
-    'attachments_link_attached' => '링크가 성공적으로 페이지에 첨부되었습니다.',
-    'templates' => 'Templates',
-    'templates_set_as_template' => 'Page is a template',
-    'templates_explain_set_as_template' => 'You can set this page as a template so its contents be utilized when creating other pages. Other users will be able to use this template if they have view permissions for this page.',
-    'templates_replace_content' => 'Replace page content',
-    'templates_append_content' => 'Append to page content',
-    'templates_prepend_content' => 'Prepend to page content',
+    'attachments_edit_file_name' => '파일 이름',
+    'attachments_edit_drop_upload' => '여기에 파일을 드롭하거나 여기를 클릭하세요. 파일을 올리거나 덮어쓸 수 있습니다.',
+    'attachments_order_updated' => '첨부 순서 바꿈',
+    'attachments_updated_success' => '첨부 파일 정보 수정함',
+    'attachments_deleted' => '첨부 파일 삭제함',
+    'attachments_file_uploaded' => '파일 올림',
+    'attachments_file_updated' => '파일 바꿈',
+    'attachments_link_attached' => '링크 첨부함',
+    'templates' => '템플릿',
+    'templates_set_as_template' => '템플릿',
+    'templates_explain_set_as_template' => '템플릿은 보기 권한만 있어도 문서에 쓸 수 있습니다.',
+    'templates_replace_content' => '문서 대체',
+    'templates_append_content' => '문서 앞에 추가',
+    'templates_prepend_content' => '문서 뒤에 추가',
 
     // Profile View
-    'profile_user_for_x' => ':time 전에 작성',
-    'profile_created_content' => '생성한 컨텐츠',
-    'profile_not_created_pages' => ':userName가 작성한 페이지가 없습니다.',
-    'profile_not_created_chapters' => ':userName가 작성한 챕터가 없습니다.',
-    'profile_not_created_books' => ':userName가 작성한 책이 없습니다.',
-    'profile_not_created_shelves' => ':userName has not created any shelves',
+    'profile_user_for_x' => ':time 전에 가입함',
+    'profile_created_content' => '활동한 이력',
+    'profile_not_created_pages' => ':userName(이)가 만든 문서 없음',
+    'profile_not_created_chapters' => ':userName(이)가 만든 챕터 없음',
+    'profile_not_created_books' => ':userName(이)가 만든 책자 없음',
+    'profile_not_created_shelves' => ':userName(이)가 만든 서가 없음',
 
     // Comments
-    'comment' => '코멘트',
-    'comments' => '코멘트들',
-    'comment_add' => '코멘트 추가',
-    'comment_placeholder' => 'ì\97¬ê¸°ì\97\90 ì½\94ë©\98í\8a¸ë¥¼ ë\82¨ê¸°ì\84¸ì\9a\94',
-    'comment_count' => '{0} 코멘트 없음|{1} 1개 코멘트|[2,*] :count개의 코멘트',
-    'comment_save' => '코멘트 저장',
-    'comment_saving' => 'ì½\94ë©\98í\8a¸ ì \80ì\9e¥중...',
-    'comment_deleting' => 'ì½\94ë©\98í\8a¸ ì\82­ì \9c중...',
-    'comment_new' => '새로운 코멘트',
-    'comment_created' => '코멘트를 작성하였습니다. :createDiff',
-    'comment_updated' => ':username이 코멘트를 수정하였습니다 :updateDiff',
-    'comment_deleted_success' => '코멘트 삭제성공',
-    'comment_created_success' => '코멘트 추가성공',
-    'comment_updated_success' => '코멘트 업데이트 성공',
-    'comment_delete_confirm' => 'ì \95ë§\90ë¡\9c ì½\94ë©\98í\8a¸ë¥¼ ì§\80ì\9a°ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'comment_in_reply_to' => ':commentId 응답',
+    'comment' => '댓글',
+    'comments' => '댓글',
+    'comment_add' => '댓글 쓰기',
+    'comment_placeholder' => 'ì\9d´ê³³ì\97\90 ë\8c\93ê¸\80ì\9d\84 ì\93°ì\84¸ì\9a\94...',
+    'comment_count' => '{0} 댓글 없음|{1} 댓글 1개|[2,*] 댓글 :count개',
+    'comment_save' => '등록',
+    'comment_saving' => 'ì \80ì\9e¥í\95\98ë\8a\94 중...',
+    'comment_deleting' => 'ì\82­ì \9cí\95\98ë\8a\94 중...',
+    'comment_new' => '새로운 댓글',
+    'comment_created' => '댓글 등록함 :createDiff',
+    'comment_updated' => ':username(이)가 댓글 수정함 :updateDiff',
+    'comment_deleted_success' => '댓글 지움',
+    'comment_created_success' => '댓글 등록함',
+    'comment_updated_success' => '댓글 수정함',
+    'comment_delete_confirm' => 'ì\9d´ ë\8c\93ê¸\80ì\9d\84 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'comment_in_reply_to' => ':commentId(을)를 향한 답글',
 
     // Revision
-    'revision_delete_confirm' => '해당 개정판을 지우시겠습니까??',
-    'revision_restore_confirm' => 'Are you sure you want to restore this revision? The current page contents will be replaced.',
-    'revision_delete_success' => '개정판 삭제성공',
-    'revision_cannot_delete_latest' => '최신버전은 지울수 없습니다.'
+    'revision_delete_confirm' => '이 수정본을 지울 건가요?',
+    'revision_restore_confirm' => '이 수정본을 되돌릴 건가요? 현재 판본을 바꿉니다.',
+    'revision_delete_success' => '수정본 지움',
+    'revision_cannot_delete_latest' => '현재 판본은 지울 수 없습니다.'
 ];
\ No newline at end of file
index 1a88dd4e31017b148e3ead49a0545d3dd9b3f119..5982a09da4c3f8ed92f17812a1e0b804d0b91d31 100644 (file)
@@ -5,81 +5,81 @@
 return [
 
     // Permissions
-    'permission' => '요청한 페이지에 권한이 없습니다.',
-    'permissionJson' => '요청한 작업을 수행 할 권한이 없습니다.',
+    'permission' => '권한이 없습니다.',
+    'permissionJson' => '권한이 없습니다.',
 
     // Auth
-    'error_user_exists_different_creds' => '전자 메일 :email을 가진 사용자가 이미 존재하지만 자격 증명이 다릅니다.',
-    'email_already_confirmed' => '이메일이 이미 확인되었습니다. 로그인 해주세요.',
-    'email_confirmation_invalid' => '이 확인 토큰이 유효하지 않거나 이미 사용되었습니다. 다시 등록하세요.',
-    'email_confirmation_expired' => 'í\99\95ì\9d¸ í\86 í\81°ì\9d´ ë§\8cë£\8cë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤. ì\83\88 í\99\95ì\9d¸ ì\9d´ë©\94ì\9d¼ì\9d´ ì \84ì\86¡ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'ldap_fail_anonymous' => '익명 바인드를 이용한 LDAP 액세스에 실패하였습니다.',
-    'ldap_fail_authed' => '주ì\96´ì§\84 dn ë°\8f ë¹\84ë°\80ë²\88í\98¸ ì\84¸ë¶\80 ì \95보를 ì\82¬ì\9a©í\95\98ì\97¬ LDAP ì\95¡ì\84¸ì\8a¤í\95\98ë\8a\94 ê²\83ì\9d´ ì\8b¤í\8c¨í\96\88습니다.',
-    'ldap_extension_not_installed' => 'LDAP PHP 확장기능이 설치되지 않았습니다.',
-    'ldap_cannot_connect' => 'LDAP 서버에 연결할 수 없습니다. 초기 연결에 실패했습니다.',
-    'social_no_action_defined' => 'ë\8f\99ì\9e\91ì\9d´ ì \95ì\9d\98ë\90\98ì§\80 ì\95\8aì\95\98습니다.',
-    'social_login_bad_response' => ":socialAccount 로그인에 실패하였습니다 : \n:error",
-    'social_account_in_use' => '이 :socialAccount 계정이 이미 사용 중입니다. :socialAccount 옵션을 통해 로그인하십시오.',
-    'social_account_email_in_use' => ' 이메일 :email이 이미 사용 중입니다. 이미 계정이있는 경우 프로필 설정에서 :socialAccount 계정을 연결할 수 있습니다.',
-    'social_account_existing' => ':socialAccount가 이미 프로필에 첨부되어 있습니다.',
-    'social_account_already_used_existing' => '이 :socialAccount 계정은 이미 다른 사용자가 사용하고 있습니다.',
-    'social_account_not_used' => '이 :socialAccount 계정이 모든 사용자에게 연결되어 있지 않습니다. 프로필 설정에 첨부하십시오. ',
-    'social_account_register_instructions' => '아직 계정이없는 경우 :socialAccount 옵션을 사용하여 계정을 등록 할 수 있습니다.',
-    'social_driver_not_found' => '소셜 드라이버를 찾을 수 없음',
-    'social_driver_not_configured' => '귀하의 :socialAccount 소셜 설정이 올바르게 구성되지 않았습니다.',
-    'invite_token_expired' => 'This invitation link has expired. You can instead try to reset your account password.',
+    'error_user_exists_different_creds' => ':email(을)를 가진 다른 사용자가 있습니다.',
+    'email_already_confirmed' => '확인이 끝난 메일 주소입니다. 로그인하세요.',
+    'email_confirmation_invalid' => '이 링크는 더 이상 유효하지 않습니다. 다시 가입하세요.',
+    'email_confirmation_expired' => 'ì\9d´ ë§\81í\81¬ë\8a\94 ë\8d\94 ì\9d´ì\83\81 ì\9c í\9a¨í\95\98ì§\80 ì\95\8aì\8aµë\8b\88ë\8b¤. ë©\94ì\9d¼ì\9d\84 ë\8b¤ì\8b\9c ë³´ë\83\88ì\8aµë\8b\88ë\8b¤.',
+    'ldap_fail_anonymous' => '익명 정보로 LDAP 서버에 접근할 수 없습니다.',
+    'ldap_fail_authed' => 'ì\9d´ ì \95ë³´ë¡\9c LDAP ì\84\9cë²\84ì\97\90 ì \91ê·¼í\95  ì\88\98 ì\97\86습니다.',
+    'ldap_extension_not_installed' => 'PHP에 LDAP 확장 도구를 설치하세요.',
+    'ldap_cannot_connect' => 'LDAP 서버에 연결할 수 없습니다.',
+    'social_no_action_defined' => '무ì\8a¨ í\99\9cë\8f\99ì\9d¸ì§\80 ì\95\8c ì\88\98 ì\97\86습니다.',
+    'social_login_bad_response' => ':socialAccount에 로그인할 수 없습니다. : \n:error',
+    'social_account_in_use' => ':socialAccount(을)를 가진 사용자가 있습니다. :socialAccount로 로그인하세요.',
+    'social_account_email_in_use' => ':email(을)를 가진 사용자가 있습니다. 쓰고 있는 계정을 :socialAccount에 연결하세요.',
+    'social_account_existing' => ':socialAccount(와)과 연결 상태입니다.',
+    'social_account_already_used_existing' => ':socialAccount(와)과 연결한 다른 계정이 있습니다.',
+    'social_account_not_used' => ':socialAccount(와)과 연결한 계정이 없습니다. 쓰고 있는 계정을 연결하세요.',
+    'social_account_register_instructions' => '계정이 없어도 :socialAccount로 가입할 수 있습니다.',
+    'social_driver_not_found' => '가입할 수 없습니다.',
+    'social_driver_not_configured' => ':socialAccount가 유효하지 않습니다.',
+    'invite_token_expired' => '이 링크는 더 이상 유효하지 않습니다. 비밀번호를 바꾸세요.',
 
     // System
-    'path_not_writable' => '파일 경로 :filePath에 업로드 할 수 없습니다. 서버에 쓰기 기능이 활성화 되어있는지 확인하세요.',
-    'cannot_get_image_from_url' => ':url에서 이미지를 가져올 수 없습니다.',
-    'cannot_create_thumbs' => 'ì\84\9cë²\84ì\97\90ì\84\9c ì\8d¸ë\84¤ì\9d¼ì\9d\84 ì\83\9dì\84±í\95  ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. GD PHPí\99\95ì\9e¥ê¸°ë\8a¥ì\9d´ ì\84¤ì¹\98ë\90\98ì\96´ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸하세요.',
-    'server_upload_limit' => 'í\95´ë\8b¹ í\81¬ê¸°ì\9d\98 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94ê²\83ì\9d´ ì\84\9cë²\84ì\97\90ì\84\9c ì \9cí\95\9cë\90©ë\8b\88ë\8b¤. í\8c\8cì\9d¼ ì\82¬ì\9d´ì¦\88를 ì\9e\91ê²\8c ì¤\84ì\9d´ê±°ë\82\98 ì\84\9cë²\84 ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\84¸ì\9a\94.',
-    'uploaded'  => 'í\95´ë\8b¹ í\81¬ê¸°ì\9d\98 í\8c\8cì\9d¼ì\9d\84 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94ê²\83ì\9d´ ì\84\9cë²\84ì\97\90ì\84\9c ì \9cí\95\9cë\90©ë\8b\88ë\8b¤. í\8c\8cì\9d¼ ì\82¬ì\9d´ì¦\88를 ì\9e\91ê²\8c ì¤\84ì\9d´ê±°ë\82\98 ì\84\9cë²\84 ì\84¤ì \95ì\9d\84 ë³\80ê²½í\95\98ì\84¸ì\9a\94.',
-    'image_upload_error' => 'ì\9d´ë¯¸ì§\80를 ì\97\85ë¡\9cë\93\9cí\95\98ë\8a\94 ì¤\91ì\97\90 ì\98¤ë¥\98ê°\80 ë°\9cì\83\9dí\96\88습니다.',
-    'image_upload_type_error' => 'ì\97\85ë¡\9cë\93\9cì¤\91ì\9d¸ ì\9d´ë¯¸ì§\80 ì\9c í\98\95ì\9d´ ì\9e\98못ë\90\98ì\97\88ì\8aµ니다.',
-    'file_upload_timeout' => '파일 업로드가 시간 초과되었습니다.',
+    'path_not_writable' => ':filePath에 쓰는 것을 서버에서 허용하지 않습니다.',
+    'cannot_get_image_from_url' => ':url에서 이미지를 불러올 수 없습니다.',
+    'cannot_create_thumbs' => 'ì\84¬ë\84¤ì\9d¼ì\9d\84 ëª» ë§\8cë\93¤ì\97\88ì\8aµë\8b\88ë\8b¤. PHPì\97\90 GD í\99\95ì\9e¥ ë\8f\84구를 ì\84¤ì¹\98하세요.',
+    'server_upload_limit' => 'í\8c\8cì\9d¼ í\81¬ê¸°ê°\80 ì\84\9cë²\84ì\97\90ì\84\9c í\97\88ì\9a©í\95\98ë\8a\94 ì\88\98ì¹\98를 ë\84\98ì\8aµë\8b\88ë\8b¤.',
+    'uploaded'  => 'í\8c\8cì\9d¼ í\81¬ê¸°ê°\80 ì\84\9cë²\84ì\97\90ì\84\9c í\97\88ì\9a©í\95\98ë\8a\94 ì\88\98ì¹\98를 ë\84\98ì\8aµë\8b\88ë\8b¤.',
+    'image_upload_error' => 'ì\9d´ë¯¸ì§\80를 ì\98¬ë¦¬ë\8b¤ ë¬¸ì \9cê°\80 ì\83\9dê²¼습니다.',
+    'image_upload_type_error' => 'ì\9c í\9a¨í\95\98ì§\80 ì\95\8aì\9d\80 ì\9d´ë¯¸ì§\80 í\98\95ì\8b\9dì\9e\85니다.',
+    'file_upload_timeout' => '파일을 올리는 데 걸리는 시간이 서버에서 허용하는 수치를 넘습니다.',
 
     // Attachments
-    'attachment_page_mismatch' => '첨ë¶\80 í\8c\8cì\9d¼ ì\97\85ë\8d°ì\9d´í\8a¸ ì¤\91 í\8e\98ì\9d´ì§\80 ë¶\88ì\9d¼ì¹\98í\95\98ì\98\80ì\8aµ니다.',
-    'attachment_not_found' => '첨ë¶\80 í\8c\8cì\9d¼ì\9d\84 ì°¾ì\9d\84 ì\88\98 없습니다.',
+    'attachment_page_mismatch' => 'ì\98¬ë¦¬ë\8a\94 ì\9c\84ì¹\98ì\99\80 í\98\84ì\9e¬ ë¬¸ì\84\9cê°\80 ë\8b¤ë¦\85니다.',
+    'attachment_not_found' => '첨ë¶\80 í\8c\8cì\9d¼ì\9d´ 없습니다.',
 
     // Pages
-    'page_draft_autosave_fail' => 'ì´\88ì\95\88ì\9d\84 ì \80ì\9e¥í\95\98ì§\80 ëª»í\96\88ì\8aµë\8b\88ë\8b¤. ì\9d´ í\8e\98ì\9d´ì§\80를 ì \80ì\9e¥í\95\98기 ì \84ì\97\90 ì\9d¸í\84°ë\84·ì\97\90 ì\97°ê²°ë\90\98ì\96´ ì\9e\88ë\8a\94ì§\80 í\99\95ì\9d¸í\95\98ì\8b­ì\8b\9cì\98¤.',
-    'page_custom_home_deletion' => '홈페이지로 설정되어있는 페이지는 삭제할 수 없습니다.',
+    'page_draft_autosave_fail' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9c를 ì\9c ì\8b¤í\96\88ì\8aµë\8b\88ë\8b¤. ì\9d¸í\84°ë\84· ì\97°ê²° ì\83\81í\83\9c를 í\99\95ì\9d¸í\95\98ì\84¸ì\9a\94.',
+    'page_custom_home_deletion' => '처음 페이지는 지울 수 없습니다.',
 
     // Entities
-    'entity_not_found' => '개체(Entity)를 찾을 수 없음.',
-    'bookshelf_not_found' => 'ì±\85ê½\82ì\9d´ë¥¼ ì°¾ì\9d\84 ì\88\98 ì\97\86ì\9d\8c.',
-    'book_not_found' => 'ì±\85ì\9d\84 ì°¾ì\9d\84 ì\88\98 ì\97\86ì\9d\8c.',
-    'page_not_found' => '페이지를 찾을 수 없음.',
-    'chapter_not_found' => '챕터를 찾을 수 없음.',
-    'selected_book_not_found' => '선택한 책을 찾을 수 없습니다.',
-    'selected_book_chapter_not_found' => '선택한 책 또는 챕터를 찾을 수 없습니다.',
-    'guests_cannot_save_drafts' => '게스트는 임시저장을 할 수 없습니다.',
+    'entity_not_found' => '항목이 없습니다.',
+    'bookshelf_not_found' => 'ì\84\9cê°\80ê°\80 ì\97\86ì\8aµë\8b\88ë\8b¤.',
+    'book_not_found' => 'ì±\85ì\9e\90ê°\80 ì\97\86ì\8aµë\8b\88ë\8b¤.',
+    'page_not_found' => '문서가 없습니다.',
+    'chapter_not_found' => '챕터가 없습니다.',
+    'selected_book_not_found' => '고른 책자가 없습니다.',
+    'selected_book_chapter_not_found' => '고른 책자나 챕터가 없습니다.',
+    'guests_cannot_save_drafts' => 'Guest는 쓰다 만 문서를 보관할 수 없습니다.',
 
     // Users
-    'users_cannot_delete_only_admin' => '어드민 계정은 삭제할 수 없습니다.',
-    'users_cannot_delete_guest' => '게스트 사용자는 삭제할 수 없습니다.',
+    'users_cannot_delete_only_admin' => 'Admin을 삭제할 수 없습니다.',
+    'users_cannot_delete_guest' => 'Guest를 삭제할 수 없습니다.',
 
     // Roles
-    'role_cannot_be_edited' => '역할을 수정할 수 없습니다.',
-    'role_system_cannot_be_deleted' => 'ì\9d´ ì\97­í\95 ì\9d\80 ì\8b\9cì\8a¤í\85\9c ì\97­í\95 ì\9e\85ë\8b\88ë\8b¤. ì\82­ì \9cí\95  수 없습니다.',
-    'role_registration_default_cannot_delete' => '이 역할은 기본 등록 역할로 설정되어있는 동안 삭제할 수 없습니다.',
-    'role_cannot_remove_only_admin' => 'This user is the only user assigned to the administrator role. Assign the administrator role to another user before attempting to remove it here.',
+    'role_cannot_be_edited' => '권한을 수정할 수 없습니다.',
+    'role_system_cannot_be_deleted' => 'ì\8b\9cì\8a¤í\85\9c ê¶\8cí\95\9cì\9d\84 ì§\80ì\9a¸ 수 없습니다.',
+    'role_registration_default_cannot_delete' => '가입한 사용자의 기본 권한을 지울 수 있어야 합니다.',
+    'role_cannot_remove_only_admin' => 'Admin을 가진 사용자가 적어도 한 명 있어야 합니다.',
 
     // Comments
-    'comment_list' => '댓글을 가져 오는 중에 오류가 발생했습니다.',
-    'cannot_add_comment_to_draft' => 'ì´\88ì\95\88ì\97\90 ì£¼ì\84\9dì\9d\84 ì¶\94ê°\80 í\95  수 없습니다.',
-    'comment_add' => '댓글을 추가 / 업데이트하는 중에 오류가 발생했습니다.',
-    'comment_delete' => 'ë\8c\93ê¸\80ì\9d\84 ì\82­ì \9cí\95\98ë\8a\94 ì¤\91ì\97\90 ì\98¤ë¥\98ê°\80 ë°\9cì\83\9dí\96\88습니다.',
-    'empty_comment' => '빈 주석을 추가 할 수 없습니다.',
+    'comment_list' => '댓글을 가져오다 문제가 생겼습니다.',
+    'cannot_add_comment_to_draft' => 'ì\93°ë\8b¤ ë§\8c ë¬¸ì\84\9cì\97\90 ë\8c\93ê¸\80ì\9d\84 ë\8b¬ 수 없습니다.',
+    'comment_add' => '댓글을 등록하다 문제가 생겼습니다.',
+    'comment_delete' => 'ë\8c\93ê¸\80ì\9d\84 ì§\80ì\9a°ë\8b¤ ë¬¸ì \9cê°\80 ì\83\9dê²¼습니다.',
+    'empty_comment' => '빈 댓글은 등록할 수 없습니다.',
 
     // Error pages
-    '404_page_not_found' => '페이지를 찾을 수 없습니다.',
-    'sorry_page_not_found' => '죄송합니다, 찾고 있던 페이지를 찾을 수 없습니다.',
-    'return_home' => 'home으로 가기',
-    'error_occurred' => '오류가 발생하였습니다.',
-    'app_down' => ':appName가 다운되었습니다.',
-    'back_soon' => 'ê³§ ë³µêµ¬ë\90  ì\98\88ì \95ì\9e\85니다.',
+    '404_page_not_found' => '404 Not Found',
+    'sorry_page_not_found' => '문서를 못 찾았습니다.',
+    'return_home' => '처음으로 돌아가기',
+    'error_occurred' => '문제가 생겼습니다.',
+    'app_down' => ':appName에 문제가 있는 것 같습니다',
+    'back_soon' => 'ê³§ ë\90\98ë\8f\8cì\95\84ê°\91니다.',
 
 ];
index 26790e95ebed87e0d23cc176c324253b7e799b8b..144736c3ba8173e2b36ef9cba6035d33827492d6 100644 (file)
@@ -6,10 +6,10 @@
  */
 return [
 
-    'password' => 'ë¹\84ë°\80ë²\88í\98¸ë\8a\94 6ì\9e\90 ì\9d´ì\83\81ì\9d´ì\96´ì\95¼ í\95\98ë©° í\99\95ì\9d¸ê³¼ ì\9d¼ì¹\98í\95´ì\95¼ í\95©ë\8b\88ë\8b¤.',
-    'user' => "해당 이메일 주소의 사용자가 없습니다.",
-    'token' => '해당 비밀번호의 초기화 토큰이 만료되었습니다.',
-    'sent' => '페스워드 초기화 링크를 메일로 보냈습니다!',
-    'reset' => '비밀번호가 초기화 되었습니다!',
+    'password' => 'ì\97¬ë\8d\9f ê¸\80ì\9e\90를 ë\84\98ì\96´ì\95¼ í\95©ë\8b\88ë\8b¤.',
+    'user' => '메일 주소를 가진 사용자가 없습니다.',
+    'token' => '이 링크는 더 이상 유효하지 않습니다.',
+    'sent' => '메일을 보냈습니다.',
+    'reset' => '비밀번호를 바꿨습니다.',
 
 ];
index 346e7f6a8462d08ea8adc38b91b72f33d8f47770..712902b667e8f05c68d7c403aa9445a331321982 100755 (executable)
@@ -8,135 +8,136 @@ return [
 
     // Common Messages
     'settings' => '설정',
-    'settings_save' => 'ì\84¤ì \95 ì \80ì\9e¥',
-    'settings_save_success' => '설정이 저장되었습니다.',
+    'settings_save' => 'ì \81ì\9a©',
+    'settings_save_success' => '설정 적용함',
 
     // App Settings
-    'app_customization' => 'Customization',
-    'app_features_security' => 'Features & Security',
-    'app_name' => 'ì\96´í\94\8c리ì¼\80ì\9d´ì\85\98 ì\9d´ë¦\84',
-    'app_name_desc' => '해당 이름은 헤더와 모든 이메일에 표시됩니다.',
-    'app_name_header' => '헤더에 어플리케이션 이름을 표시하시겠습니까?',
-    'app_public_access' => 'Public Access',
-    'app_public_access_desc' => 'Enabling this option will allow visitors, that are not logged-in, to access content in your BookStack instance.',
-    'app_public_access_desc_guest' => 'Access for public visitors can be controlled through the "Guest" user.',
-    'app_public_access_toggle' => 'Allow public access',
-    'app_public_viewing' => '공개 보기를 허용하시겠습니까?',
-    'app_secure_images' => '더 높은 보안 이미지 업로드를 사용하시겠습니까?',
-    'app_secure_images_toggle' => 'Enable higher security image uploads',
-    'app_secure_images_desc' => '성능상의 이유로 모든 이미지를 공개합니다. 해당 옵션은 이미지 URL 앞에 추측하기 어려운 임의의 문자열을 추가합니다. 간편한 접근을 방지하기 위해 디렉토리 색인을 비활성화하십시오.',
-    'app_editor' => '페이지 에디터',
-    'app_editor_desc' => '모든 사용자ê°\80 í\8e\98ì\9d´ì§\80를 í\8e¸ì§\91í\95\98ë\8a\94ë\8d° ì\82¬ì\9a©í\95  ì\97\90ë\94\94í\84°ë¥¼ ì\84 í\83\9dí\95\98ì\8b­ì\8b\9cì\98¤.',
-    'app_custom_html' => '사용자 정의 HTML 헤드 컨텐츠',
-    'app_custom_html_desc' => 'ì\97¬ê¸°ì\97\90 ì¶\94ê°\80ë\90\9c ëª¨ë\93  ë\82´ì\9a©ì\9d\80 ëª¨ë\93  í\8e\98ì\9d´ì§\80ì\9d\98 <head> ì\84¹ì\85\98 ì\95\84ë\9e\98쪽ì\97\90 ì\82½ì\9e\85ë\90©ë\8b\88ë\8b¤. ì\9d´ë\8a\94 ì\8a¤í\83\80ì\9d¼ ì\98¤ë²\84ë\9d¼ì\9d´ë\94©ì\9d´ë\82\98 ë¶\84ì\84\9d ì½\94ë\93\9c ì\82½ì\9e\85ì\97\90 í\8e¸ë¦¬합니다.',
-    'app_custom_html_disabled_notice' => 'Custom HTML head content is disabled on this settings page to ensure any breaking changes can be reverted.',
-    'app_logo' => 'ì\96´í\94\8c리ì¼\80ì\9d´ì\85\98 로고',
-    'app_logo_desc' => 'í\95´ë\8b¹ ì\9d´ë¯¸ì§\80ë\8a\94 ë°\98ë\93\9cì\8b\9c ë\86\92ì\9d´ê°\80 43í\94½ì\85\80ì\9d´ì\96´ì\95¼ í\95©ë\8b\88ë\8b¤. <br>ë\8c\80ì\9a©ë\9f\89 ì\9d´ë¯¸ì§\80ë\8a\94 ì¶\95ì\86\8cë\90©ë\8b\88ë\8b¤.',
-    'app_primary_color' => 'ì\96´í\94\8c리ì¼\80ì\9d´ì\85\98 ê¸°ë³¸ ì\83\89ì\83\81',
-    'app_primary_color_desc' => 'í\95´ë\8b¹ ê°\92ì\9d\80 16ì§\84ì\88\98ì\9d´ì\96´ì\95¼ í\95©ë\8b\88ë\8b¤. <br>ì\9e\85ë ¥í\95\98ì§\80 ì\95\8aì\9c¼ë©´ ê¸°ë³¸ ì\83\89ì\83\81ì\9c¼ë¡\9c ì\9e¬ì\84¤ì \95ë\90©ë\8b\88ë\8b¤.',
-    'app_homepage' => 'ì\96´í\94\8c리ì¼\80ì\9d´ì\85\98 í\99\88페이지',
-    'app_homepage_desc' => '기본 í\99\94ë©´ ë\8c\80ì\8b ì\97\90 í\99\88í\8e\98ì\9d´ì§\80ì\97\90 í\91\9cì\8b\9cí\95  í\99\94ë©´ì\9d\84 ì\84 í\83\9dí\95\98ì\8b­ì\8b\9cì\98¤. ì\84 í\83\9dë\90\9c í\8e\98ì\9d´ì§\80ì\97\90ì\84\9cë\8a\94 í\8e\98ì\9d´ì§\80 ê¶\8cí\95\9cì\9d´ ë¬´ì\8b\9cë\90©ë\8b\88ë\8b¤.',
-    'app_homepage_select' => '페이지를 선택하십시오',
-    'app_disable_comments' => '주석 비활성화',
-    'app_disable_comments_toggle' => 'Disable comments',
-    'app_disable_comments_desc' => '어플리케이션의 모든 페이지에서 주석을 비활성화합니다. 기존의 주석은 표시되지 않습니다.',
+    'app_customization' => '맞춤',
+    'app_features_security' => '보안',
+    'app_name' => 'ì\82¬ì\9d´í\8a¸ ì \9c목',
+    'app_name_desc' => '메일을 보낼 때 이 제목을 씁니다.',
+    'app_name_header' => '사이트 헤더 사용',
+    'app_public_access' => '사이트 공개',
+    'app_public_access_desc' => '계정 없는 사용자가 문서를 볼 수 있습니다.',
+    'app_public_access_desc_guest' => '이들의 권한은 사용자 이름이 Guest인 사용자로 관리할 수 있습니다.',
+    'app_public_access_toggle' => '사이트 공개',
+    'app_public_viewing' => '공개할 건가요?',
+    'app_secure_images' => '이미지 주소 보호',
+    'app_secure_images_toggle' => '이미지 주소 보호',
+    'app_secure_images_desc' => '성능상의 문제로 이미지에 누구나 접근할 수 있기 때문에 이미지 주소를 무작위한 문자로 구성합니다. 폴더 색인을 끄세요.',
+    'app_editor' => '에디터',
+    'app_editor_desc' => '모든 사용자ì\97\90ê²\8c ì \81ì\9a©í\95©ë\8b\88ë\8b¤.',
+    'app_custom_html' => '헤드 작성',
+    'app_custom_html_desc' => 'ì\84¤ì \95 í\8e\98ì\9d´ì§\80를 ì \9cì\99¸í\95\9c ëª¨ë\93  í\8e\98ì\9d´ì§\80 head í\83\9cê·¸ ë\81\9d머리ì\97\90 ì¶\94ê°\80합니다.',
+    'app_custom_html_disabled_notice' => '문제가 생겨도 설정 페이지에서 되돌릴 수 있어요.',
+    'app_logo' => 'ì\82¬ì\9d´í\8a¸ 로고',
+    'app_logo_desc' => 'ë\86\92ì\9d´ë¥¼ 43pxë¡\9c êµ¬ì\84±í\95\98ì\84¸ì\9a\94. í\81° ì\9d´ë¯¸ì§\80ë\8a\94 ì¶\95ì\86\8cí\95©ë\8b\88ë\8b¤.',
+    'app_primary_color' => 'ì\82¬ì\9d´í\8a¸ ì\83\89ì±\84',
+    'app_primary_color_desc' => '16ì§\84ì\88\98ë¡\9c êµ¬ì\84±í\95\98ì\84¸ì\9a\94. ë¹\84ì\9b ì\9d\84 ë\95\8cë\8a\94 ê¸°ë³¸ ì\83\89ì±\84ë¡\9c ì\84¤ì \95í\95©ë\8b\88ë\8b¤.',
+    'app_homepage' => 'ì²\98ì\9d\8c 페이지',
+    'app_homepage_desc' => '고른 í\8e\98ì\9d´ì§\80ì\97\90 ì\84¤ì \95í\95\9c ê¶\8cí\95\9cì\9d\80 ë¬´ì\8b\9cí\95©ë\8b\88ë\8b¤.',
+    'app_homepage_select' => '문서 고르기',
+    'app_disable_comments' => '댓글 사용 안 함',
+    'app_disable_comments_toggle' => '댓글 사용 안 함',
+    'app_disable_comments_desc' => '모든 페이지에서 댓글을 숨깁니다.',
 
     // Registration Settings
-    'reg_settings' => '등록 설정',
-    'reg_enable' => 'Enable Registration',
-    'reg_enable_toggle' => 'Enable registration',
-    'reg_enable_desc' => 'When registration is enabled user will be able to sign themselves up as an application user. Upon registration they are given a single, default user role.',
-    'reg_default_role' => '등록 후 기본 사용자 역할',
-    'reg_email_confirmation' => 'Email Confirmation',
-    'reg_email_confirmation_toggle' => 'Require email confirmation',
-    'reg_confirm_email_desc' => 'ë\8f\84ë©\94ì\9d¸ ì \9cí\95\9cì\9d´ ì\82¬ì\9a©ë\90\98ë©´ ì\9d´ë©\94ì\9d¼ í\99\95ì\9d¸ì\9d´ ì\9a\94구ë\90\98ë©°, í\95\98ë\8b¨ì\9d\98 ê°\92ì\9d\80 ë¬´ì\8b\9cë\90©ë\8b\88ë\8b¤.',
-    'reg_confirm_restrict_domain' => '도메인 등록 제한',
-    'reg_confirm_restrict_domain_desc' => '등록을 제한할 이메일 도메인의 목록을 쉼표로 구분하여 입력해주십시오. 사용자는 어플리케이션과의 상호작용을 허가받기 전에 이메일 주소를 확인하는 이메일을 받게 됩니다, <br> 등록이 완료된 후에는 이메일 주소를 변경할 수 있습니다.',
-    'reg_confirm_restrict_domain_placeholder' => 'ì \9cí\95\9c ì\97\86ì\9d\8c ì\84¤ì \95',
+    'reg_settings' => '가입',
+    'reg_enable' => '사이트 가입 허용',
+    'reg_enable_toggle' => '사이트 가입 허용',
+    'reg_enable_desc' => '가입한 사용자는 단일한 권한을 가집니다.',
+    'reg_default_role' => '가입한 사용자의 기본 권한',
+    'reg_email_confirmation' => '메일 주소 확인',
+    'reg_email_confirmation_toggle' => '주소 확인 요구',
+    'reg_confirm_email_desc' => 'ë\8f\84ë©\94ì\9d¸ ì°¨ë\8b¨ì\9d\84 ì\93°ê³  ì\9e\88ì\9c¼ë©´ ë©\94ì\9d¼ ì£¼ì\86\8c를 í\99\95ì\9d¸í\95´ì\95¼ í\95\98ê³ , ì\9d´ ì\84¤ì \95ì\9d\80 ë¬´ì\8b\9cí\95©ë\8b\88ë\8b¤.',
+    'reg_confirm_restrict_domain' => '도메인 차단',
+    'reg_confirm_restrict_domain_desc' => '쉼표로 분리해서 가입을 차단할 메일 주소 도메인을 쓰세요. 이 설정과 관계없이 사용자가 메일을 보내고, 가입한 사용자가 메일 주소를 바꿀 수 있습니다.',
+    'reg_confirm_restrict_domain_placeholder' => 'ì\97\86ì\9d\8c',
 
     // Maintenance settings
-    'maint' => 'Maintenance',
+    'maint' => '데이터',
     'maint_image_cleanup' => '이미지 정리',
-    'maint_image_cleanup_desc' => "페이지를 스캔하여 현재 사용중인 이미지와 도면에서 수정된 내용 및 중복된 이미지를 확인합니다. 이를 실행하기 전에 전체 데이터베이스와 이미지의 백업을 작성했는지 확인하십시오.",
-    'maint_image_cleanup_ignore_revisions' => 'ì\88\98ì \95본ì\9d\98 ì\9d´ë¯¸ì§\80를 ë¬´ì\8b\9cí\95©ë\8b\88ë\8b¤.',
-    'maint_image_cleanup_run' => 'ì \95리 ì\8b¤í\96\89',
-    'maint_image_cleanup_warning' => 'ì\9e ì\9e¬ì \81ì\9c¼ë¡\9c ì\82¬ì\9a©ë\90\98ì§\80 ì\95\8aë\8a\94 ì\9d´ë¯¸ì§\80를 ì°¾ì\95\98ì\8aµë\8b\88ë\8b¤. í\95´ë\8b¹ ì\9d´ë¯¸ì§\80ë\93¤ì\9d\84 ì\82­ì \9cí\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'maint_image_cleanup_success' => ':잠재적으로 사용되지 않는 이미지들이 삭제되었습니다.',
-    'maint_image_cleanup_nothing_found' => 'ì\82¬ì\9a©ë\90\98ì§\80 ì\95\8aë\8a\94 ì\9d´ë¯¸ì§\80를 ì°¾ì\9d\84 ì\88\98 ì\97\86ì\8aµë\8b\88ë\8b¤. ì\95\84무ê²\83ë\8f\84 ì\82­ì \9cë\90\98ì§\80 ì\95\8aì\95\98ì\8aµë\8b\88ë\8b¤.',
+    'maint_image_cleanup_desc' => '중복한 이미지를 찾습니다. 실행하기 전에 이미지를 백업하세요.',
+    'maint_image_cleanup_ignore_revisions' => 'ì\88\98ì \95본ì\97\90 ì\9e\88ë\8a\94 ì\9d´ë¯¸ì§\80 ì \9cì\99¸',
+    'maint_image_cleanup_run' => '실행',
+    'maint_image_cleanup_warning' => 'ì\9d´ë¯¸ì§\80 :countê°\9c를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'maint_image_cleanup_success' => '이미지 :count개 삭제함',
+    'maint_image_cleanup_nothing_found' => 'ì\82­ì \9cí\95\9c ê²\83 ì\97\86ì\9d\8c',
 
     // Role Settings
-    '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' => '역할명',
-    'role_desc' => 'ì\97­í\95 ì\97\90 ë\8c\80í\95\9c ê°\84ë\9eµí\95\9c ì\84¤ëª\85',
-    'role_external_auth_id' => '외부 인증 ID',
+    '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' => '권한 이름',
+    'role_desc' => '설명',
+    'role_external_auth_id' => 'LDAP 확인',
     'role_system' => '시스템 권한',
     'role_manage_users' => '사용자 관리',
-    'role_manage_roles' => '역할 및 역할 권한 관리',
-    'role_manage_entity_permissions' => '모ë\93  ì±\85, ì±\95í\84°, í\8e\98ì\9d´ì§\80 관리',
-    'role_manage_own_entity_permissions' => '보유한 책, 챕터, 페이지에 대한 권한 관리',
-    'role_manage_page_templates' => 'Manage page templates',
-    'role_manage_settings' => 'ì\96´í\94\8c리ì¼\80ì\9d´ì\84  설정 관리',
-    'role_asset' => '자산 관리',
-    'role_asset_desc' => '해당 권한들은 시스템 내의 Assets 파일에 대한 기본적인 접근을 제어합니다.',
-    'role_asset_admins' => '관리자는 모든 컨텐츠에 대한 접근 권한을 자동으로 부여받지만, 해당 옵션들은 UI 옵션을 표시하거나 숨길 수 있습니다.',
-    'role_all' => '전체',
-    'role_own' => '보유한 것만',
-    'role_controlled_by_asset' => 'ì\97\85ë¡\9cë\93\9cë\90\9c Assets í\8c\8cì\9d¼ì\97\90 ì\9d\98í\95´ ì \9cì\96´ë\90©ë\8b\88ë\8b¤.',
-    'role_save' => 'ì\97­í\95  ì \80ì\9e¥',
-    'role_update_success' => '역할이 업데이트되었습니다.',
-    'role_users' => '해당 역할의 사용자',
-    'role_users_none' => '현재 이 역할에 할당된 사용자가 없습니다.',
+    'role_manage_roles' => '권한 관리',
+    'role_manage_entity_permissions' => '문ì\84\9cë³\84 ê¶\8cí\95\9c 관리',
+    'role_manage_own_entity_permissions' => '직접 만든 문서별 권한 관리',
+    'role_manage_page_templates' => '템플릿 관리',
+    'role_manage_settings' => 'ì\82¬ì\9d´í\8a¸ 설정 관리',
+    'role_asset' => '권한 항목',
+    'role_asset_desc' => '책자, 챕터, 문서별 권한은 이 설정에 우선합니다.',
+    'role_asset_admins' => 'Admin 권한은 어디든 접근할 수 있지만 이 설정은 사용자 인터페이스에서 해당 활동을 표시할지 결정합니다.',
+    'role_all' => '모든 항목',
+    'role_own' => '직접 만든 항목',
+    'role_controlled_by_asset' => 'ì \80ë§\88ë\8b¤ ë\8b¤ë¦\84',
+    'role_save' => '저장',
+    'role_update_success' => '권한 저장함',
+    'role_users' => '이 권한을 가진 사용자들',
+    'role_users_none' => '그런 사용자가 없습니다.',
 
     // Users
     'users' => '사용자',
     'user_profile' => '사용자 프로필',
-    'users_add_new' => '사용자 추가',
+    'users_add_new' => '사용자 만들기',
     'users_search' => '사용자 검색',
-    'users_details' => 'User Details',
-    'users_details_desc' => 'Set a display name and an email address for this user. The email address will be used for logging into the application.',
-    'users_details_desc_no_email' => 'Set a display name for this user so others can recognise them.',
-    'users_role' => '사용자 역할',
-    'users_role_desc' => 'Select which roles this user will be assigned to. If a user is assigned to multiple roles the permissions from those roles will stack and they will receive all abilities of the assigned roles.',
-    'users_password' => 'User Password',
-    'users_password_desc' => 'Set a password used to log-in to the application. This must be at least 6 characters long.',
-    'users_send_invite_text' => 'You can choose to send this user an invitation email which allows them to set their own password otherwise you can set their password yourself.',
-    'users_send_invite_option' => 'Send user invite email',
-    'users_external_auth_id' => '외부 인증 ID',
-    'users_external_auth_id_desc' => 'This is the ID used to match this user when communicating with your LDAP system.',
-    'users_password_warning' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ë³\80ê²½í\95\98ì\8b\9c려면 ë\8b¤ì\9d\8cì\9d\84 ì\9e\85ë ¥í\95\98ì\8b­ì\8b\9cì\98¤:',
-    'users_system_public' => '이 사용자는 당신의 인스턴스를 방문하는 게스트 사용자를 나타냅니다. 로그인하는 데는 사용할 수 없지만 자동으로 할당됩니다.',
+    'users_details' => '사용자 정보',
+    'users_details_desc' => '메일 주소로 로그인합니다.',
+    'users_details_desc_no_email' => '사용자 이름을 바꿉니다.',
+    'users_role' => '사용자 권한',
+    'users_role_desc' => '고른 권한 모두를 적용합니다.',
+    'users_password' => '비밀번호',
+    'users_password_desc' => '여섯 글자를 넘어야 합니다.',
+    'users_send_invite_text' => '비밀번호 설정을 권유하는 메일을 보내거나 내가 정할 수 있습니다.',
+    'users_send_invite_option' => '메일 보내기',
+    'users_external_auth_id' => 'LDAP 확인',
+    'users_external_auth_id_desc' => 'LDAP 서버에 연결할 때 사용자를 확인합니다.',
+    'users_password_warning' => 'ë¹\84ë°\80ë²\88í\98¸ë¥¼ ë°\94ê¿\80 ë\95\8cë§\8c ì\93°ì\84¸ì\9a\94.',
+    'users_system_public' => '계정 없는 모든 사용자에 할당한 사용자입니다. 이 사용자로 로그인할 수 없어요.',
     'users_delete' => '사용자 삭제',
-    'users_delete_named' => '사용자 :userName 삭제',
-    'users_delete_warning' => '시스템에서 \':userName\'(이)라는 사용자가 완전히 삭제됩니다.',
-    'users_delete_confirm' => 'ì\9d´ ì\82¬ì\9a©ì\9e\90를 ì\82­ì \9cí\95\98ì\8b\9cê² ì\8aµë\8b\88ê¹\8c?',
-    'users_delete_success' => '사용자가 삭제되었습니다.',
-    'users_edit' => '사용자 편집',
-    'users_edit_profile' => '프로필 편집',
-    'users_edit_success' => '사용자가 업데이트되었습니다.',
-    'users_avatar' => 'ì\82¬ì\9a©ì\9e\90 ì\95\84ë°\94í\83\80',
-    'users_avatar_desc' => '해당 이미지는 256픽셀의 정사각형 이미지여야합니다.',
-    'users_preferred_language' => 'ì\84 í\98¸í\95\98ë\8a\94 ì\96¸ì\96´',
-    'users_preferred_language_desc' => 'This option will change the language used for the user-interface of the application. This will not affect any user-created content.',
+    'users_delete_named' => ':userName 삭제',
+    'users_delete_warning' => ':userName에 관한 데이터를 지웁니다.',
+    'users_delete_confirm' => 'ì\9d´ ì\82¬ì\9a©ì\9e\90를 ì§\80ì\9a¸ ê±´ê°\80ì\9a\94?',
+    'users_delete_success' => '사용자 삭제함',
+    'users_edit' => '사용자 수정',
+    'users_edit_profile' => '프로필 바꾸기',
+    'users_edit_success' => '프로필 바꿈',
+    'users_avatar' => 'í\94\84ë¡\9cí\95\84 ì\9d´ë¯¸ì§\80',
+    'users_avatar_desc' => '이미지 규격은 256x256px 내외입니다.',
+    'users_preferred_language' => '언어',
+    'users_preferred_language_desc' => '문서 내용에는 아무런 영향을 주지 않습니다.',
     'users_social_accounts' => '소셜 계정',
-    'users_social_accounts_info' => 'ì\97¬ê¸°ì\97\90ì\84\9c ë\8b¤ë¥¸ ê³\84ì \95ì\9d\84 ì\97°ê²°í\95\98ì\97¬ ë\8d\94 ë¹ ë¥´ê³  ì\89½ê²\8c ë¡\9cê·¸ì\9d¸í\95  ì\88\98 ì\9e\88ì\8aµë\8b\88ë\8b¤. ì\97¬ê¸°ì\97\90ì\84\9c ê³\84ì \95 ì\97°ê²°ì\9d\84 í\95´ì \9cí\95\98ë©´ ì\9d´ì \84ì\97\90 ì\8a¹ì\9d¸ë\90\9c ì \91ê·¼ì\9d´ ì \9cê³µë\90\98ì§\80 ì\95\8aì\8aµë\8b\88ë\8b¤ ì\97°ê²°ë\90\9c ì\86\8cì\85\9c ê³\84ì \95ì\9d\98 í\94\84ë¡\9cí\95\84 ì\84¤ì \95ì\97\90ì\84\9c ì \91ê·¼ ê¶\8cí\95\9cì\9d\84 ì·¨ì\86\8cí\95\98ì\8b­ì\8b\9cì\98¤.',
+    'users_social_accounts_info' => 'ë\8b¤ë¥¸ ê³\84ì \95ì\9c¼ë¡\9c ê°\84ë\8b¨í\95\98ê²\8c ë¡\9cê·¸ì\9d¸í\95\98ì\84¸ì\9a\94. ì\97¬ê¸°ì\97\90ì\84\9c ê³\84ì \95 ì\97°ê²°ì\9d\84 ë\81\8aë\8a\94 ê²\83ê³¼ ì\86\8cì\85\9c ê³\84ì \95ì\97\90ì\84\9c ì \91ê·¼ ê¶\8cí\95\9cì\9d\84 ì·¨ì\86\8cí\95\98ë\8a\94 ê²\83ì\9d\80 ë³\84ê°\9cì\9e\85ë\8b\88ë\8b¤.',
     'users_social_connect' => '계정 연결',
-    'users_social_disconnect' => '계정 연결 해제',
-    'users_social_connected' => ':socialAccount ê³\84ì \95ì\9d´ ë\8b¹ì\8b ì\9d\98 í\94\84ë¡\9cí\95\84ì\97\90 ì\97°ê²°ë\90\98ì\97\88ì\8aµë\8b\88ë\8b¤.',
-    'users_social_disconnected' => ':socialAccount 계정이 당신의 프로필에서 연결해제되었습니다.',
+    'users_social_disconnect' => '계정 연결 끊기',
+    'users_social_connected' => ':socialAccount\99\80)ê³¼ ì\97°ê²°í\96\88ì\8aµë\8b\88ë\8b¤.',
+    'users_social_disconnected' => ':socialAccount(와)과의 연결을 끊었습니다.',
 
-    //! If editing translations files directly please ignore this in all
-    //! languages apart from en. Content will be auto-copied from en.
+    //! Since these labels are already localized this array does not need to be
+    //! translated in the language-specific files.
+    //! DELETE BELOW IF COPIED FROM EN
     //!////////////////////////////////
     'language_select' => [
         'en' => 'English',
index 07ecdd021e4127cb52cbb819b96d8beee6b48bf9..180faa35f377afa18958033edcdbf51416124f9a 100644 (file)
 return [
 
     // Standard laravel validation lines
-    'accepted'             => ':attribute가 반드시 허용되어야 합니다.',
-    'active_url'           => ':attribute가 올바른 URL이 아닙니다.',
-    'after'                => ':attribute는 :date이후 날짜여야 합니다.',
-    'alpha'                => ':attribute는 문자만 포함해야 합니다.',
-    'alpha_dash'           => ':attribute는 문자, 숫자, 대시만 포함해야 합니다.',
-    'alpha_num'            => ':attribute는 문자와 숫자만 포함됩니다.',
-    'array'                => ':attribute는 배열이어야 합니다.',
-    'before'               => ':attribute는 :date이전 날짜여야 합니다.',
+    'accepted'             => ':attribute(을)를 허용하세요.',
+    'active_url'           => ':attribute(을)를 유효한 주소로 구성하세요.',
+    'after'                => ':attribute(을)를 :date 후로 설정하세요.',
+    'alpha'                => ':attribute(을)를 문자로만 구성하세요.',
+    'alpha_dash'           => ':attribute(을)를 문자, 숫자, -, _로만 구성하세요.',
+    'alpha_num'            => ':attribute(을)를 문자, 숫자로만 구성하세요.',
+    'array'                => ':attribute(을)를 배열로 구성하세요.',
+    'before'               => ':attribute(을)를 :date 전으로 설정하세요.',
     'between'              => [
-        'numeric' => ':attribute는 반드시 :min이상 :max이하여야 합니다.',
-        'file'    => ':attribute는 반드시 :min이상 :max kilobytes이하여야 합니다.',
-        'string'  => ':attribute는 반드시 :min이상 :max 문자 이하여야 합니다.',
-        'array'   => ':attribute는 반드시 :min이상 :max이하 항목이어야 합니다.',
+        'numeric' => ':attribute(을)를 :min~:max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 :min~:max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 :min~:max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 :min~:max개로 구성하세요.',
     ],
-    'boolean'              => ':attribute 는 true혹은 false값만 가능합니다.',
-    'confirmed'            => ':attribute 확인이 일치하지 않습니다.',
-    'date'                 => ':attribute 는 잘못된 날짜입니다.',
-    'date_format'          => ':attribute 이 :format 포멧과 일치하지 않습니다.',
-    'different'            => ':attribute 와 :other는 반드시 달라야 합니다.',
-    'digits'               => ':attribute 는 반드시 :digits 숫자(digit)여야 합니다.',
-    'digits_between'       => ':attribute 는 반드시 :min이상 :max이하 숫자여야 합니다.',
-    'email'                => ':attribute 는 반드시 이메일 이어야 합니다.',
-    'ends_with' => 'The :attribute must end with one of the following: :values',
-    'filled'               => ':attribute 항목이 꼭 필요합니다.',
+    '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.',
-        'array'   => 'The :attribute must have more than :value items.',
+        'numeric' => ':attribute(을)를 :value(이)가 넘게 구성하세요.',
+        'file'    => ':attribute(을)를 :value킬로바이트가 넘게 구성하세요.',
+        'string'  => ':attribute(을)를 :value바이트가 넘게 구성하세요.',
+        'array'   => ':attribute(을)를 :value개가 넘게 구성하세요.',
     ],
     'gte'                  => [
-        'numeric' => 'The :attribute must be greater than or equal :value.',
-        'file'    => 'The :attribute must be greater than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be greater than or equal :value characters.',
-        'array'   => 'The :attribute must have :value items or more.',
+        'numeric' => ':attribute(을)를 적어도 :value(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 적어도 :value킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 적어도 :value바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 적어도 :value개로 구성하세요..',
     ],
-    'exists'               => '선택된 :attribute 은(는) 사용 불가합니다.',
-    'image'                => ':attribute 는 반드시 이미지여야 합니다.',
-    'image_extension'      => 'The :attribute must have a valid & supported image extension.',
-    'in'                   => '선택된 :attribute 은(는) 사용 불가합니다.',
-    'integer'              => ':attribute 는 반드시(integer)여야 합니다.',
-    'ip'                   => ':attribute 는 반드시 IP주소 여야 합니다.',
-    '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(을)를 이미지로 구성하세요.',
+    '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.',
-        'array'   => 'The :attribute must have less than :value items.',
+        'numeric' => ':attribute(을)를 :value(이)가 안 되게 구성하세요.',
+        'file'    => ':attribute(을)를 :value킬로바이트가 안 되게 구성하세요.',
+        'string'  => ':attribute(을)를 :value바이트가 안 되게 구성하세요.',
+        'array'   => ':attribute(을)를 :value개가 안 되게 구성하세요.',
     ],
     'lte'                  => [
-        'numeric' => 'The :attribute must be less than or equal :value.',
-        'file'    => 'The :attribute must be less than or equal :value kilobytes.',
-        'string'  => 'The :attribute must be less than or equal :value characters.',
-        'array'   => 'The :attribute must not have more than :value items.',
+        'numeric' => ':attribute(을)를 많아야 :max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 많아야 :max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 많아야 :max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 많아야 :max개로 구성하세요.',
     ],
     'max'                  => [
-        'numeric' => ':attribute :max 보다 크면 안됩니다.',
-        'file'    => ':attribute :max kilobytes보다 크면 안됩니다.',
-        'string'  => ':attribute :max 문자보다 길면 안됩니다.',
-        'array'   => ':attribute :max 를 초과하면 안됩니다.',
+        'numeric' => ':attribute(을)를 많아야 :max(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 많아야 :max킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 많아야 :max바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 많아야 :max개로 구성하세요.',
     ],
-    'mimes'                => ':attribute 은(는) 반드시 :values 타입이어야 합니다.',
+    'mimes'                => ':attribute(을)를 :values 형식으로 구성하세요.',
     'min'                  => [
-        'numeric' => ':attribute 은(는) 최소한 :min 이어야 합니다.',
-        'file'    => ':attribute 은(는) 최소한 :min kilobytes여야 합니다.',
-        'string'  => ':attribute 은(는) 최소한 :min 개 문자여야 합니다.',
-        'array'   => ':attribute 은(는) 적어도 :min 개의 항목이어야 합니다.',
+        'numeric' => ':attribute(을)를 적어도 :value(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 적어도 :value킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 적어도 :value바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 적어도 :value개로 구성하세요..',
     ],
-    'no_double_extension'  => 'The :attribute must only have a single file extension.',
-    'not_in'               => '선택된 :attribute 는 사용할 수 없습니다',
-    'not_regex'            => 'The :attribute format is invalid.',
-    'numeric'              => ':attribute 반드시 숫자여야 합니다.',
-    'regex'                => ':attribute 포멧이 잘못되었습니다.',
-    'required'             => ':attribute 항목은 필수입니다..',
-    'required_if'          => ':attribute 은(는) :other 가 :value 일때 필수항목입니다.',
-    'required_with'        => ':attribute 은(는) :values 가 있을때 필수항목입니다.',
-    'required_with_all'    => ':attribute 은(는) :values 가 있을때 필수항목입니다.',
-    'required_without'     => ':attribute 은(는) :values 가 없을때 필수항목입니다.',
-    'required_without_all' => ':attribute 은(는) :values 가 전혀 없을때 필수항목입니다.',
-    'same'                 => ':attribute 와 :other 은(는) 반드시 일치해야합니다.',
+    'no_double_extension'  => ':attribute(이)가 단일한 확장자를 가져야 합니다.',
+    'not_in'               => '고른 :attribute(이)가 유효하지 않습니다.',
+    'not_regex'            => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'numeric'              => ':attribute(을)를 숫자로만 구성하세요.',
+    'regex'                => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'required'             => ':attribute(을)를 구성하세요.',
+    'required_if'          => ':other(이)가 :value일 때 :attribute(을)를 구성해야 합니다.',
+    'required_with'        => ':values(이)가 있을 때 :attribute(을)를 구성해야 합니다.',
+    'required_with_all'    => ':values(이)가 모두 있을 때 :attribute(을)를 구성해야 합니다.',
+    'required_without'     => ':values(이)가 없을 때 :attribute(을)를 구성해야 합니다.',
+    'required_without_all' => ':values(이)가 모두 없을 때 :attribute(을)를 구성해야 합니다.',
+    'same'                 => ':attribute(와)과 :other(을)를 똑같이 구성하세요.',
     'size'                 => [
-        'numeric' => ':attribute 은(는) :size 여야합니다.',
-        'file'    => ':attribute 은(는) :size kilobytes여야합니다.',
-        'string'  => ':attribute 은(는) :size 문자여야합니다.',
-        'array'   => ':attribute 은(는) :size 개 항목을 포함해야 합니다.',
+        'numeric' => ':attribute(을)를 :size(으)로 구성하세요.',
+        'file'    => ':attribute(을)를 :size킬로바이트로 구성하세요.',
+        'string'  => ':attribute(을)를 :size바이트로 구성하세요.',
+        'array'   => ':attribute(을)를 :size개로 구성하세요..',
     ],
-    'string'               => ':attribute 문자열이어야 합니다.',
-    'timezone'             => ':attribute 정상적인 지역(zone)이어야 합니다.',
-    'unique'               => ':attribute 은(는) 이미 사용중입니다..',
-    'url'                  => ':attribute 포멧이 사용 불가합니다.',
-    'uploaded'             => 'The file could not be uploaded. The server may not accept files of this size.',
+    'string'               => ':attribute(을)를 문자로 구성하세요.',
+    'timezone'             => ':attribute(을)를 유효한 시간대로 구성하세요.',
+    'unique'               => ':attribute(은)는 이미 있습니다.',
+    'url'                  => ':attribute(은)는 유효하지 않은 형식입니다.',
+    'uploaded'             => '파일 크기가 서버에서 허용하는 수치를 넘습니다.',
 
     // Custom validation lines
     'custom' => [
         'password-confirm' => [
-            'required_with' => '비밀번호 확인이 필요합니다.',
+            'required_with' => '같은 비밀번호를 다시 입력하세요.',
         ],
     ],
 
index e3f7028eb349c1566019aff588df91656310d8b5..28c7776083c86687cececd0aebaf6bf75dff8251 100644 (file)
@@ -403,6 +403,7 @@ span.CodeMirror-selectedtext { background: none; }
   left: 0;
   width: 100%;
   height: 100%;
+  margin-bottom: 0;
 }
 
 /**
index 8623d374af4b1f63ffa4a9f8a352b26b1de6247a..77f51b3243eb96a9473721eb5adc344c4779b273 100644 (file)
   fill: #575757 !important;
 }
 
+.text-dark {
+  color: #222 !important;
+  fill: #222 !important;
+}
+
 /*
  * Entity text colors
  */
index 64308b29e725bbe0470189b0e259e31630ee0b73..3e7ff60f357c9598f7f450a57371200dfc25405f 100644 (file)
     width: 50%;
     max-width: 50%;
   }
+  &.fullscreen {
+    position: fixed;
+    top: 0;
+    left: 0;
+    height: 100%;
+    z-index: 2;
+  }
 }
 
 @include smaller-than($m) {
@@ -71,6 +78,7 @@
   #markdown-editor .markdown-editor-wrap {
     width: 100%;
     max-width: 100%;
+    flex-grow: 1;
   }
   #markdown-editor .editor-toolbar {
     padding: 0;
     border-bottom: 1px solid #DDD;
     display: block;
   }
-  .markdown-editor-wrap:not(.active) .editor-toolbar + div, .markdown-editor-wrap:not(.active) .editor-toolbar .buttons {
+  .markdown-editor-wrap:not(.active) .editor-toolbar + div,
+  .markdown-editor-wrap:not(.active) .editor-toolbar .buttons,
+  .markdown-editor-wrap:not(.active) .markdown-display {
     display: none;
   }
   #markdown-editor .markdown-editor-wrap:not(.active) {
     flex-grow: 0;
     flex: none;
+    min-height: 0;
   }
 }
 
@@ -172,6 +183,11 @@ input[type=date] {
 
 input[type=color] {
   height: 60px;
+  &.small {
+    height: 42px;
+    width: 60px;
+    padding: 2px;
+  }
 }
 
 .toggle-switch {
index de48c8ed1bbe8eac685067c399c583faafe0abdc..e4a8c14bb8faf0b75bc3abeb0e1ad0d10320eb46 100644 (file)
@@ -1,6 +1,7 @@
 * {
   box-sizing: border-box;
-  outline-color: #444444;
+  outline-color: var(--color-primary);
+  outline-width: 1px;
 }
 
 *:focus {
index 709b1a7efe845b85dd6c55027d6d7be2fd5d3f37..9281a21946db4c75a6211cdf2ee20568640cd6b7 100755 (executable)
   }
 }
 
-body.mce-fullscreen .page-editor .edit-area {
+body.mce-fullscreen .page-editor .edit-area,
+body.markdown-fullscreen .page-editor .edit-area {
   z-index: 12;
 }
 
+body.mce-fullscreen, body.markdown-fullscreen {
+  .page-editor, .flex-fill {
+    overflow: visible;
+  }
+}
+
 @include smaller-than($s) {
   .page-edit-toolbar {
     overflow-x: scroll;
index fbf540d7105fd6f0969956d7291413471d820b53..098ce2100d31b9a4193ec345525146103edec0fa 100644 (file)
                 @endforeach
             @endif
 
-            @if(setting('registration-enabled', false))
+            @if($samlEnabled)
+                <hr class="my-l">
+                <div>
+                    <a id="saml-login" class="button outline block svg" href="{{ url("/saml2/login") }}">
+                       @icon('saml2')
+                      {{ trans('auth.log_in_with', ['socialDriver' => config('saml2.name')]) }}
+                    </a>
+                </div>
+            @endif
+
+            @if(setting('registration-enabled') && config('auth.method') !== 'ldap')
                 <div class="text-center pb-s">
                     <hr class="my-l">
                     <a href="{{ url('/register') }}">{{ trans('auth.dont_have_account') }}</a>
@@ -54,4 +64,4 @@
         </div>
     </div>
 
-@stop
\ No newline at end of file
+@stop
index 0e996a00d2300e1d27ecba57976260af441e6ee0..8dd6592c1e72084f4acba1059a56f226c80102d7 100644 (file)
                     </div>
                 @endforeach
             @endif
+
+            @if($samlEnabled)
+                <hr class="my-l">
+                <div>
+                    <a id="saml-login" class="button outline block svg" href="{{ url("/saml2/login") }}">
+                        @icon('saml2')
+                        {{ trans('auth.log_in_with', ['socialDriver' => config('saml2.name')]) }}
+                    </a>
+                </div>
+            @endif
         </div>
     </div>
 @stop
index 19299695042e98ded5b55735865c31f6fee9528b..b060360315eba00b62bae30193b0ba7f304ae06c 100644 (file)
@@ -42,7 +42,7 @@
                     @endif
 
                     @if(!signedInUser())
-                        @if(setting('registration-enabled', false))
+                        @if(setting('registration-enabled') && config('auth.method') !== 'ldap')
                             <a href="{{ url('/register') }}">@icon('new-user') {{ trans('auth.sign_up') }}</a>
                         @endif
                         <a href="{{ url('/login') }}">@icon('login') {{ trans('auth.log_in') }}</a>
diff --git a/resources/views/components/setting-entity-color-picker.blade.php b/resources/views/components/setting-entity-color-picker.blade.php
new file mode 100644 (file)
index 0000000..fffb422
--- /dev/null
@@ -0,0 +1,21 @@
+{{--
+    @type - Name of entity type
+--}}
+<div setting-color-picker class="grid no-break half mb-l">
+    <div>
+        <label for="setting-{{ $type }}-color" class="text-dark">{{ trans('settings.'. str_replace('-', '_', $type) .'_color') }}</label>
+        <button type="button" class="text-button text-muted" setting-color-picker-default>{{ trans('common.default') }}</button>
+        <span class="sep mx-xs">|</span>
+        <button type="button" class="text-button text-muted" setting-color-picker-reset>{{ trans('common.reset') }}</button>
+    </div>
+    <div>
+        <input type="color"
+               data-default="{{ config('setting-defaults.'. $type .'-color') }}"
+               data-current="{{ setting($type .'-color') }}"
+               value="{{ setting($type .'-color') }}"
+               name="setting-{{ $type }}-color"
+               id="setting-{{ $type }}-color"
+               placeholder="{{ config('setting-defaults.'. $type .'-color') }}"
+               class="small">
+    </div>
+</div>
\ No newline at end of file
index 52644113802d419ed0e8bdafaede843dc0110fe4..85afbea069ab4726c6588971445e439d65ebb5d2 100644 (file)
@@ -9,17 +9,21 @@
             <div class="float right buttons">
                 @if(config('services.drawio'))
                     <button class="text-button" type="button" data-action="insertDrawing">@icon('drawing'){{ trans('entities.pages_md_insert_drawing') }}</button>
-                    &nbsp;|&nbsp
+                    <span class="mx-xs text-muted">|</span>
                 @endif
                 <button class="text-button" type="button" data-action="insertImage">@icon('image'){{ trans('entities.pages_md_insert_image') }}</button>
-                &nbsp;|&nbsp;
+                <span class="mx-xs text-muted">|</span>
                 <button class="text-button" type="button" data-action="insertLink">@icon('link'){{ trans('entities.pages_md_insert_link') }}</button>
+                <span class="mx-xs text-muted">|</span>
+                <button class="text-button" type="button" data-action="fullscreen">@icon('fullscreen'){{ trans('common.fullscreen') }}</button>
             </div>
         </div>
 
         <div markdown-input class="flex flex-fill">
-                        <textarea  id="markdown-editor-input"  name="markdown" rows="5"
-                                   @if($errors->has('markdown')) class="text-neg" @endif>@if(isset($model) || old('markdown')){{ old('markdown') ? old('markdown') : ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
+            <textarea id="markdown-editor-input"
+                      @if($errors->has('markdown')) class="text-neg" @endif
+                      name="markdown"
+                      rows="5">@if(isset($model) || old('markdown')){{ old('markdown') ?? ($model->markdown === '' ? $model->html : $model->markdown) }}@endif</textarea>
         </div>
 
     </div>
index 51ab5bbbe53531e315e6ae314694123adcfc7281..dfe0a9d7d2fbe28d6e9b9129c66ce72ac1811412 100644 (file)
     </div>
 
     <div class="actions mb-xl">
-        <h5>Actions</h5>
+        <h5>{{ trans('common.actions') }}</h5>
 
         <div class="icon-list text-primary">
 
index 9080790825f74c4014f53368458f7c3fe3705570..f819391098022152e96be375893cb21b259832e3 100644 (file)
@@ -2,5 +2,10 @@
     :root {
         --color-primary: {{ setting('app-color') }};
         --color-primary-light: {{ setting('app-color-light') }};
+        --color-bookshelf: {{ setting('bookshelf-color')}};
+        --color-book: {{ setting('book-color')}};
+        --color-chapter: {{ setting('chapter-color')}};
+        --color-page: {{ setting('page-color')}};
+        --color-page-draft: {{ setting('page-draft-color')}};
     }
-</style>
\ No newline at end of file
+</style>
index a5277418661ea29af619b3f9fb58a0abadd13de8..94605da6f11029c946c25032949a14ffea971ba2 100644 (file)
                         </div>
                     </div>
 
+                    <!-- Primary Color -->
                     <div class="grid half gap-xl">
                         <div>
                             <label class="setting-list-label">{{ trans('settings.app_primary_color') }}</label>
                             <p class="small">{!! trans('settings.app_primary_color_desc') !!}</p>
                         </div>
                         <div setting-app-color-picker class="text-m-right">
-                            <input type="color" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
+                            <input type="color" data-default="#206ea7" data-current="{{ setting('app-color') }}" value="{{ setting('app-color') }}" name="setting-app-color" id="setting-app-color" placeholder="#206ea7">
                             <input type="hidden" value="{{ setting('app-color-light') }}" name="setting-app-color-light" id="setting-app-color-light">
                             <br>
+                            <button type="button" class="text-button text-muted mt-s mx-s" setting-app-color-picker-default>{{ trans('common.default') }}</button>
+                            <span class="sep">|</span>
                             <button type="button" class="text-button text-muted mt-s mx-s" setting-app-color-picker-reset>{{ trans('common.reset') }}</button>
                         </div>
                     </div>
 
+                    <!-- Entity Color -->
+                    <div class="pb-l">
+                        <div>
+                            <label class="setting-list-label">{{ trans('settings.content_colors') }}</label>
+                            <p class="small">{!! trans('settings.content_colors_desc') !!}</p>
+                        </div>
+                        <div class="grid half pt-m">
+                            <div>
+                                @include('components.setting-entity-color-picker', ['type' => 'bookshelf'])
+                                @include('components.setting-entity-color-picker', ['type' => 'book'])
+                                @include('components.setting-entity-color-picker', ['type' => 'chapter'])
+                            </div>
+                            <div>
+                                @include('components.setting-entity-color-picker', ['type' => 'page'])
+                                @include('components.setting-entity-color-picker', ['type' => 'page-draft'])
+                            </div>
+                        </div>
+                    </div>
+
                     <div homepage-control id="homepage-control" class="grid half gap-xl">
                         <div>
                             <label for="setting-app-homepage" class="setting-list-label">{{ trans('settings.app_homepage') }}</label>
                                 'label' => trans('settings.reg_enable_toggle')
                             ])
 
+                            @if(config('auth.method') === 'ldap')
+                                <div class="text-warn text-small mb-l">{{ trans('settings.reg_enable_ldap_warning') }}</div>
+                            @endif
+
                             <label for="setting-registration-role">{{ trans('settings.reg_default_role') }}</label>
                             <select id="setting-registration-role" name="setting-registration-role" @if($errors->has('setting-registration-role')) class="neg" @endif>
                                 @foreach(\BookStack\Auth\Role::all() as $role)
 
     @include('components.image-manager', ['imageType' => 'system'])
     @include('components.entity-selector-popup', ['entityTypes' => 'page'])
-@stop
\ No newline at end of file
+@stop
index 20b8d65ed9c22fd12a2050d6e2f2ba3a060d04d5..4617b1f520d26bf65fe69b5b15311d06059d192d 100644 (file)
@@ -19,7 +19,7 @@
                     @include('form.text', ['name' => 'description'])
                 </div>
 
-                @if(config('auth.method') === 'ldap')
+                @if(config('auth.method') === 'ldap' || config('saml2.enabled') === true)
                     <div class="form-group">
                         <label for="name">{{ trans('settings.role_external_auth_id') }}</label>
                         @include('form.text', ['name' => 'external_auth_id'])
             {{ trans('settings.role_users_none') }}
         </p>
     @endif
-</div>
\ No newline at end of file
+</div>
index 32b717ec8c2ec4db4ef10211f54f0b1aab30f054..6eafd43bcfd98e1bb28af610d9683b6c4bb7225f 100644 (file)
@@ -25,7 +25,7 @@
     </div>
 </div>
 
-@if($authMethod === 'ldap' && userCan('users-manage'))
+@if(($authMethod === 'ldap' || config('saml2.enabled') === true) && userCan('users-manage'))
     <div class="grid half gap-xl v-center">
         <div>
             <label class="setting-list-label">{{ trans('settings.users_external_auth_id') }}</label>
@@ -84,4 +84,4 @@
         </div>
 
     </div>
-@endif
\ No newline at end of file
+@endif
index eafb6a45cbc6f91d1881f453439d53902437d218..839e5a256f7684590961377cad9d98928c974580 100644 (file)
@@ -216,6 +216,13 @@ Route::post('/register/confirm/resend', 'Auth\ConfirmEmailController@resend');
 Route::get('/register/confirm/{token}', 'Auth\ConfirmEmailController@confirm');
 Route::post('/register', 'Auth\RegisterController@postRegister');
 
+// SAML routes
+Route::get('/saml2/login', 'Auth\Saml2Controller@login');
+Route::get('/saml2/logout', 'Auth\Saml2Controller@logout');
+Route::get('/saml2/metadata', 'Auth\Saml2Controller@metadata');
+Route::get('/saml2/sls', 'Auth\Saml2Controller@sls');
+Route::post('/saml2/acs', 'Auth\Saml2Controller@acs');
+
 // User invitation routes
 Route::get('/register/invite/{token}', 'Auth\UserInviteController@showSetPassword');
 Route::post('/register/invite/{token}', 'Auth\UserInviteController@setPassword');
index fe28698dfac6b79013020c43fb82a93f1bdcb8fe..5d7bbfb1e41f592b05330ae3a12ca49cb3969b46 100644 (file)
@@ -24,6 +24,7 @@ class LdapTest extends BrowserKitTest
             'services.ldap.base_dn' => 'dc=ldap,dc=local',
             'services.ldap.email_attribute' => 'mail',
             'services.ldap.display_name_attribute' => 'cn',
+            'services.ldap.id_attribute' => 'uid',
             'services.ldap.user_to_groups' => false,
             'auth.providers.users.driver' => 'ldap',
         ]);
@@ -102,6 +103,32 @@ class LdapTest extends BrowserKitTest
             ->seeInDatabase('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()
+    {
+        config()->set(['services.ldap.id_attribute' => 'my_custom_id']);
+        $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
+        $this->mockLdap->shouldReceive('setVersion')->once();
+        $ldapDn = 'cn=test-user,dc=test' . config('services.ldap.base_dn');
+        $this->mockLdap->shouldReceive('setOption')->times(2);
+        $this->mockLdap->shouldReceive('searchAndGetEntries')->times(2)
+            ->with($this->resourceId, config('services.ldap.base_dn'), \Mockery::type('string'), \Mockery::type('array'))
+            ->andReturn(['count' => 1, 0 => [
+                'cn' => [$this->mockUser->name],
+                'dn' => $ldapDn,
+                'my_custom_id' => ['cooluser456'],
+                'mail' => [$this->mockUser->email]
+            ]]);
+
+
+        $this->mockLdap->shouldReceive('bind')->times(3)->andReturn(true);
+        $this->mockEscapes(2);
+
+        $this->mockUserLogin()
+            ->seePageIs('/')
+            ->see($this->mockUser->name)
+            ->seeInDatabase('users', ['email' => $this->mockUser->email, 'email_confirmed' => false, 'external_auth_id' => 'cooluser456']);
+    }
+
     public function test_initial_incorrect_details()
     {
         $this->mockLdap->shouldReceive('connect')->once()->andReturn($this->resourceId);
@@ -365,7 +392,7 @@ class LdapTest extends BrowserKitTest
                 'uid' => [$this->mockUser->name],
                 'cn' => [$this->mockUser->name],
                 'dn' => ['dc=test' . config('services.ldap.base_dn')],
-                'displayName' => 'displayNameAttribute'
+                'displayname' => 'displayNameAttribute'
             ]]);
         $this->mockLdap->shouldReceive('bind')->times(6)->andReturn(true);
         $this->mockEscapes(4);
diff --git a/tests/Auth/Saml2Test.php b/tests/Auth/Saml2Test.php
new file mode 100644 (file)
index 0000000..45b6efa
--- /dev/null
@@ -0,0 +1,315 @@
+<?php namespace Tests;
+
+use BookStack\Auth\Role;
+use BookStack\Auth\User;
+
+class Saml2Test extends TestCase
+{
+
+    public function setUp(): void
+    {
+        parent::setUp();
+        // Set default config for SAML2
+        config()->set([
+            'saml2.name' => 'SingleSignOn-Testing',
+            'saml2.enabled' => true,
+            'saml2.auto_register' => true,
+            'saml2.email_attribute' => 'email',
+            'saml2.display_name_attributes' => ['first_name', 'last_name'],
+            'saml2.external_id_attribute' => 'uid',
+            'saml2.user_to_groups' => false,
+            'saml2.group_attribute' => 'user_groups',
+            'saml2.remove_from_groups' => false,
+            'saml2.onelogin_overrides' => null,
+            'saml2.onelogin.idp.entityId' => 'http://saml.local/saml2/idp/metadata.php',
+            'saml2.onelogin.idp.singleSignOnService.url' => 'http://saml.local/saml2/idp/SSOService.php',
+            'saml2.onelogin.idp.singleLogoutService.url' => 'http://saml.local/saml2/idp/SingleLogoutService.php',
+            'saml2.autoload_from_metadata' => false,
+            'saml2.onelogin.idp.x509cert' => $this->testCert,
+            'saml2.onelogin.debug' => false,
+        ]);
+    }
+
+    public function test_metadata_endpoint_displays_xml_as_expected()
+    {
+        $req = $this->get('/saml2/metadata');
+        $req->assertHeader('Content-Type', 'text/xml; charset=UTF-8');
+        $req->assertSee('md:EntityDescriptor');
+        $req->assertSee(url('/saml2/acs'));
+    }
+
+    public function test_onelogin_overrides_functions_as_expected()
+    {
+        $json = '{"sp": {"assertionConsumerService": {"url": "https://example.com/super-cats"}}, "contactPerson": {"technical": {"givenName": "Barry Scott", "emailAddress": "[email protected]"}}}';
+        config()->set(['saml2.onelogin_overrides' => $json]);
+
+        $req = $this->get('/saml2/metadata');
+        $req->assertSee('https://example.com/super-cats');
+        $req->assertSee('md:ContactPerson');
+        $req->assertSee('<md:GivenName>Barry Scott</md:GivenName>');
+    }
+
+    public function test_login_option_shows_on_login_page()
+    {
+        $req = $this->get('/login');
+        $req->assertSeeText('SingleSignOn-Testing');
+        $req->assertElementExists('a[href$="/saml2/login"]');
+    }
+
+    public function test_login_option_shows_on_register_page_only_when_auto_register_enabled()
+    {
+        $this->setSettings(['app-public' => 'true', 'registration-enabled' => 'true']);
+
+        $req = $this->get('/register');
+        $req->assertSeeText('SingleSignOn-Testing');
+        $req->assertElementExists('a[href$="/saml2/login"]');
+
+        config()->set(['saml2.auto_register' => false]);
+
+        $req = $this->get('/register');
+        $req->assertDontSeeText('SingleSignOn-Testing');
+        $req->assertElementNotExists('a[href$="/saml2/login"]');
+    }
+
+    public function test_login()
+    {
+        $req = $this->get('/saml2/login');
+        $redirect = $req->headers->get('location');
+        $this->assertStringStartsWith('http://saml.local/saml2/idp/SSOService.php', $redirect, 'Login redirects to SSO location');
+
+        config()->set(['saml2.onelogin.strict' => false]);
+        $this->assertFalse($this->isAuthenticated());
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertRedirect('/');
+            $this->assertTrue($this->isAuthenticated());
+            $this->assertDatabaseHas('users', [
+                'email' => '[email protected]',
+                'external_auth_id' => 'user',
+                'email_confirmed' => true,
+                'name' => 'Barry Scott'
+            ]);
+
+        });
+    }
+
+    public function test_group_role_sync_on_login()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => false,
+        ]);
+
+        $memberRole = factory(Role::class)->create(['external_auth_id' => 'member']);
+        $adminRole = Role::getSystemRole('admin');
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () use ($memberRole, $adminRole) {
+            $acsPost = $this->post('/saml2/acs');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $userRoleIds = $user->roles()->pluck('id');
+            $this->assertContains($memberRole->id, $userRoleIds, 'User was assigned to member role');
+            $this->assertContains($adminRole->id, $userRoleIds, 'User was assigned to admin role');
+        });
+    }
+
+    public function test_group_role_sync_removal_option_works_as_expected()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.user_to_groups' => true,
+            'saml2.remove_from_groups' => true,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $user = User::query()->where('external_auth_id', '=', 'user')->first();
+
+            $randomRole = factory(Role::class)->create(['external_auth_id' => 'random']);
+            $user->attachRole($randomRole);
+            $this->assertContains($randomRole->id, $user->roles()->pluck('id'));
+
+            auth()->logout();
+            $acsPost = $this->post('/saml2/acs');
+            $this->assertNotContains($randomRole->id, $user->roles()->pluck('id'));
+        });
+    }
+
+    public function test_logout_redirects_to_saml_logout_when_active_saml_session()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $lastLoginType = session()->get('last_login_type');
+            $this->assertEquals('saml2', $lastLoginType);
+
+            $req = $this->get('/logout');
+            $req->assertRedirect('/saml2/logout');
+        });
+    }
+
+    public function test_logout_sls_flow()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $handleLogoutResponse = function () {
+            $this->assertTrue($this->isAuthenticated());
+
+            $req = $this->get('/saml2/sls');
+            $req->assertRedirect('/');
+            $this->assertFalse($this->isAuthenticated());
+        };
+
+        $loginAndStartLogout = function () use ($handleLogoutResponse) {
+            $this->post('/saml2/acs');
+
+            $req = $this->get('/saml2/logout');
+            $redirect = $req->headers->get('location');
+            $this->assertStringStartsWith('http://saml.local/saml2/idp/SingleLogoutService.php', $redirect);
+            $this->withGet(['SAMLResponse' => $this->sloResponseData], $handleLogoutResponse);
+        };
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], $loginAndStartLogout);
+    }
+
+    public function test_logout_sls_flow_when_sls_not_configured()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.onelogin.idp.singleLogoutService.url' => null,
+        ]);
+
+        $loginAndStartLogout = function () {
+            $this->post('/saml2/acs');
+
+            $req = $this->get('/saml2/logout');
+            $req->assertRedirect('/');
+            $this->assertFalse($this->isAuthenticated());
+        };
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], $loginAndStartLogout);
+    }
+
+    public function test_dump_user_details_option_works()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+            'saml2.dump_user_details' => true,
+        ]);
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertJsonStructure([
+                'id_from_idp',
+                'attrs_from_idp' => [],
+                'attrs_after_parsing' => [],
+            ]);
+        });
+    }
+
+    public function test_user_registration_with_existing_email()
+    {
+        config()->set([
+            'saml2.onelogin.strict' => false,
+        ]);
+
+        $viewer = $this->getViewer();
+        $viewer->email = '[email protected]';
+        $viewer->save();
+
+        $this->withPost(['SAMLResponse' => $this->acsPostData], function () {
+            $acsPost = $this->post('/saml2/acs');
+            $acsPost->assertRedirect('/');
+            $errorMessage = session()->get('error');
+            $this->assertEquals('Registration unsuccessful since a user already exists with email address "[email protected]"', $errorMessage);
+        });
+    }
+
+    public function test_saml_routes_are_only_active_if_saml_enabled()
+    {
+        config()->set(['saml2.enabled' => false]);
+        $getRoutes = ['/login', '/logout', '/metadata', '/sls'];
+        foreach ($getRoutes as $route) {
+            $req = $this->get('/saml2' . $route);
+            $req->assertRedirect('/');
+            $error = session()->get('error');
+            $this->assertStringStartsWith('You do not have permission to access', $error);
+            session()->flush();
+        }
+
+        $postRoutes = ['/acs'];
+        foreach ($postRoutes as $route) {
+            $req = $this->post('/saml2' . $route);
+            $req->assertRedirect('/');
+            $error = session()->get('error');
+            $this->assertStringStartsWith('You do not have permission to access', $error);
+            session()->flush();
+        }
+    }
+
+    protected function withGet(array $options, callable $callback)
+    {
+        return $this->withGlobal($_GET, $options, $callback);
+    }
+
+    protected function withPost(array $options, callable $callback)
+    {
+        return $this->withGlobal($_POST, $options, $callback);
+    }
+
+    protected function withGlobal(array &$global, array $options, callable $callback)
+    {
+        $original = [];
+        foreach ($options as $key => $val) {
+            $original[$key] = $global[$key] ?? null;
+            $global[$key] = $val;
+        }
+
+        $callback();
+
+        foreach ($options as $key => $val) {
+            $val = $original[$key];
+            if ($val) {
+                $global[$key] = $val;
+            } else {
+                unset($global[$key]);
+            }
+        }
+    }
+
+    /**
+     * The post data for a callback for single-sign-in.
+     * Provides the following attributes:
+     * array:5 [
+        "uid" => array:1 [
+            0 => "user"
+        ]
+        "first_name" => array:1 [
+            0 => "Barry"
+        ]
+        "last_name" => array:1 [
+            0 => "Scott"
+        ]
+        "email" => array:1 [
+            0 => "[email protected]"
+        ]
+        "user_groups" => array:2 [
+            0 => "member"
+            1 => "admin"
+        ]
+    ]
+     */
+    protected $acsPostData = 'PHNhbWxwOlJlc3BvbnNlIHhtbG5zOnNhbWxwPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6cHJvdG9jb2wiIHhtbG5zOnNhbWw9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphc3NlcnRpb24iIElEPSJfNGRkNDU2NGRjNzk0MDYxZWYxYmFhMDQ2N2Q3OTAyOGNlZDNjZTU0YmVlIiBWZXJzaW9uPSIyLjAiIElzc3VlSW5zdGFudD0iMjAxOS0xMS0xN1QxNzo1MzozOVoiIERlc3RpbmF0aW9uPSJodHRwOi8vYm9va3N0YWNrLmxvY2FsL3NhbWwyL2FjcyIgSW5SZXNwb25zZVRvPSJPTkVMT0dJTl82YTBmNGYzOTkzMDQwZjE5ODdmZDM3MDY4YjUyOTYyMjlhZDUzNjFjIj48c2FtbDpJc3N1ZXI+aHR0cDovL3NhbWwubG9jYWwvc2FtbDIvaWRwL21ldGFkYXRhLnBocDwvc2FtbDpJc3N1ZXI+PGRzOlNpZ25hdHVyZSB4bWxuczpkcz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnIyI+CiAgPGRzOlNpZ25lZEluZm8+PGRzOkNhbm9uaWNhbGl6YXRpb25NZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzEwL3htbC1leGMtYzE0biMiLz4KICAgIDxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CiAgPGRzOlJlZmVyZW5jZSBVUkk9IiNfNGRkNDU2NGRjNzk0MDYxZWYxYmFhMDQ2N2Q3OTAyOGNlZDNjZTU0YmVlIj48ZHM6VHJhbnNmb3Jtcz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz48ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+PC9kczpUcmFuc2Zvcm1zPjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz48ZHM6RGlnZXN0VmFsdWU+dm1oL1M3NU5mK2crZWNESkN6QWJaV0tKVmx1ZzdCZnNDKzlhV05lSXJlUT08L2RzOkRpZ2VzdFZhbHVlPjwvZHM6UmVmZXJlbmNlPjwvZHM6U2lnbmVkSW5mbz48ZHM6U2lnbmF0dXJlVmFsdWU+dnJhZ0tKWHNjVm5UNjJFaEk3bGk4MERUWHNOTGJOc3lwNWZ2QnU4WjFYSEtFUVA3QWpPNkcxcVBwaGpWQ2dRMzd6TldVVTZvUytQeFA3UDlHeG5xL3hKejRUT3lHcHJ5N1RoK2pIcHc0YWVzQTdrTmp6VU51UmU2c1ltWTlrRXh2VjMvTmJRZjROMlM2Y2RhRHIzWFRodllVVDcxYzQwNVVHOFJpQjJaY3liWHIxZU1yWCtXUDBnU2Qrc0F2RExqTjBJc3pVWlVUNThadFpEVE1ya1ZGL0pIbFBFQ04vVW1sYVBBeitTcUJ4c25xTndZK1oxYUt3MnlqeFRlNnUxM09Kb29OOVN1REowNE0rK2F3RlY3NkI4cXEyTzMxa3FBbDJibm1wTGxtTWdRNFEraUlnL3dCc09abTV1clphOWJObDNLVEhtTVBXbFpkbWhsLzgvMy9IT1RxN2thWGs3cnlWRHRLcFlsZ3FUajNhRUpuL0dwM2o4SFp5MUVialRiOTRRT1ZQMG5IQzB1V2hCaE13TjdzVjFrUSsxU2NjUlpUZXJKSGlSVUQvR0srTVg3M0YrbzJVTFRIL1Z6Tm9SM2o4N2hOLzZ1UC9JeG5aM1RudGR1MFZPZS9ucEdVWjBSMG9SWFhwa2JTL2poNWk1ZjU0RXN4eXZ1VEM5NHdKaEM8L2RzOlNpZ25hdHVyZVZhbHVlPgo8ZHM6S2V5SW5mbz48ZHM6WDUwOURhdGE+PGRzOlg1MDlDZXJ0aWZpY2F0ZT5NSUlFYXpDQ0F0T2dBd0lCQWdJVWU3YTA4OENucjRpem1ybkJFbng1cTNIVE12WXdEUVlKS29aSWh2Y05BUUVMQlFBd1JURUxNQWtHQTFVRUJoTUNSMEl4RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeElUQWZCZ05WQkFvTUdFbHVkR1Z5Ym1WMElGZHBaR2RwZEhNZ1VIUjVJRXgwWkRBZUZ3MHhPVEV4TVRZeE1qRTNNVFZhRncweU9URXhNVFV4TWpFM01UVmFNRVV4Q3pBSkJnTlZCQVlUQWtkQ01STXdFUVlEVlFRSURBcFRiMjFsTFZOMFlYUmxNU0V3SHdZRFZRUUtEQmhKYm5SbGNtNWxkQ0JYYVdSbmFYUnpJRkIwZVNCTWRHUXdnZ0dpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElCandBd2dnR0tBb0lCZ1FEekxlOUZmZHlwbFR4SHA0U3VROWdRdFpUM3QrU0RmdkVMNzJwcENmRlp3NytCNXM1Qi9UNzNhWHBvUTNTNTNwR0kxUklXQ2dlMmlDVVEydHptMjdhU05IMGl1OWFKWWNVUVovUklUcWQwYXl5RGtzMU5BMlBUM1RXNnQzbTdLVjVyZTRQME5iK1lEZXV5SGRreitqY010cG44Q21Cb1QwSCtza2hhMGhpcUlOa2prUlBpSHZMSFZHcCt0SFVFQS9JNm1ONGFCL1VFeFNUTHM3OU5zTFVmdGVxcXhlOSt0dmRVYVRveURQcmhQRmpPTnMrOU5LQ2t6SUM2dmN2N0o2QXR1S0c2bkVUK3pCOXlPV2d0R1lRaWZYcVFBMnk1ZEw4MUJCMHE1dU1hQkxTMnBxM2FQUGp6VTJGMytFeXNqeVNXVG5Da2ZrN0M1U3NDWFJ1OFErVTk1dHVucE5md2Y1b2xFNldhczQ4Tk1NK1B3VjdpQ05NUGtOemxscTZQQ2lNK1A4RHJNU2N6elVaWlFVU3Y2ZFN3UENvK1lTVmltRU0wT2czWEpUaU5oUTVBTmxhSW42Nkt3NWdmb0JmdWlYbXlJS2lTRHlBaURZbUZhZjQzOTV3V3dMa1RSK2N3OFdmamFIc3dLWlRvbW4xTVIzT0pzWTJVSjBlUkJZTStZU3NDQXdFQUFhTlRNRkV3SFFZRFZSME9CQllFRkltcDJDWUNHZmNiN3c5MUgvY1NoVENrWHdSL01COEdBMVVkSXdRWU1CYUFGSW1wMkNZQ0dmY2I3dzkxSC9jU2hUQ2tYd1IvTUE4R0ExVWRFd0VCL3dRRk1BTUJBZjh3RFFZSktvWklodmNOQVFFTEJRQURnZ0dCQUErZy9DN3VMOWxuK1crcUJrbkxXODFrb2pZZmxnUEsxSTFNSEl3bk12bC9aVEhYNGRSWEtEcms3S2NVcTFLanFhak5WNjZmMWNha3AwM0lpakJpTzBYaTFnWFVaWUxvQ2lOR1V5eXA5WGxvaUl5OVh3MlBpV25ydzAreVp5dlZzc2JlaFhYWUpsNFJpaEJqQld1bDlSNHdNWUxPVVNKRGUyV3hjVUJoSm54eU5ScytQMHhMU1FYNkIybjZueG9Ea280cDA3czhaS1hRa2VpWjJpd0ZkVHh6UmtHanRoTVV2NzA0bnpzVkdCVDBEQ1B0ZlNhTzVLSlpXMXJDczN5aU10aG5CeHE0cUVET1FKRklsKy9MRDcxS2JCOXZaY1c1SnVhdnpCRm1rS0dOcm8vNkcxSTdlbDQ2SVI0d2lqVHlORkNZVXVEOWR0aWduTm1wV3ROOE9XK3B0aUwvanRUeVNXdWtqeXMwcyt2TG44M0NWdmpCMGRKdFZBSVlPZ1hGZEl1aWk2Nmdjend3TS9MR2lPRXhKbjBkVE56c0ovSVlocHhMNEZCRXVQMHBza1kwbzBhVWxKMkxTMmord1NRVFJLc0JnTWp5clVyZWtsZTJPRFN0U3RuM2VhYmpJeDAvRkhscEZyMGpOSW0vb01QN2t3anRVWDR6YU5lNDdRSTRHZz09PC9kczpYNTA5Q2VydGlmaWNhdGU+PC9kczpYNTA5RGF0YT48L2RzOktleUluZm8+PC9kczpTaWduYXR1cmU+PHNhbWxwOlN0YXR1cz48c2FtbHA6U3RhdHVzQ29kZSBWYWx1ZT0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnN0YXR1czpTdWNjZXNzIi8+PC9zYW1scDpTdGF0dXM+PHNhbWw6QXNzZXJ0aW9uIHhtbG5zOnhzaT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS9YTUxTY2hlbWEtaW5zdGFuY2UiIHhtbG5zOnhzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIgSUQ9Il82ODQyZGY5YzY1OWYxM2ZlNTE5NmNkOWVmNmMyZjAyODM2NGFlOTQzYjEiIFZlcnNpb249IjIuMCIgSXNzdWVJbnN0YW50PSIyMDE5LTExLTE3VDE3OjUzOjM5WiI+PHNhbWw6SXNzdWVyPmh0dHA6Ly9zYW1sLmxvY2FsL3NhbWwyL2lkcC9tZXRhZGF0YS5waHA8L3NhbWw6SXNzdWVyPjxkczpTaWduYXR1cmUgeG1sbnM6ZHM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyMiPgogIDxkczpTaWduZWRJbmZvPjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CiAgICA8ZHM6U2lnbmF0dXJlTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxkc2lnLW1vcmUjcnNhLXNoYTI1NiIvPgogIDxkczpSZWZlcmVuY2UgVVJJPSIjXzY4NDJkZjljNjU5ZjEzZmU1MTk2Y2Q5ZWY2YzJmMDI4MzY0YWU5NDNiMSI+PGRzOlRyYW5zZm9ybXM+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvMDkveG1sZHNpZyNlbnZlbG9wZWQtc2lnbmF0dXJlIi8+PGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPjwvZHM6VHJhbnNmb3Jtcz48ZHM6RGlnZXN0TWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8wNC94bWxlbmMjc2hhMjU2Ii8+PGRzOkRpZ2VzdFZhbHVlPmtyYjV3NlM4dG9YYy9lU3daUFVPQnZRem4zb3M0SkFDdXh4ckpreHBnRnc9PC9kczpEaWdlc3RWYWx1ZT48L2RzOlJlZmVyZW5jZT48L2RzOlNpZ25lZEluZm8+PGRzOlNpZ25hdHVyZVZhbHVlPjJxcW1Ba3hucXhOa3N5eXh5dnFTVDUxTDg5VS9ZdHpja2t1ekF4ci9hQ1JTK1NPRzg1YkFNWm8vU3puc3d0TVlBYlFRQ0VGb0R1amdNdlpzSFl3NlR2dmFHanlXWUpRNVZyYWhlemZaSWlCVUU0NHBtWGFrOCswV0l0WTVndnBGSXhxWFZaRmdFUkt2VExmZVFCMzhkMVZQc0ZVZ0RYdXQ4VS9Qdm43dXZwdXZjVXorMUUyOUVKR2FZL0dndnhUN0tyWU9SQTh3SitNdVRzUVZtanNlUnhveVJTejA4TmJ3ZTJIOGpXQnpFWWNxWWwyK0ZnK2hwNWd0S216VmhLRnBkNXZBNjdBSXo1NXN0QmNHNSswNHJVaWpFSzRzci9xa0x5QmtKQjdLdkwzanZKcG8zQjhxYkxYeXhLb1dSSmRnazhKNHMvTVp1QWk3QWUxUXNTTjl2Z3ZTdVRlc0VCUjVpSHJuS1lrbEpRWXNrbUQzbSsremE4U1NRbnBlM0UzYUZBY3p6cElUdUQ4YkFCWmRqcUk2TkhrSmFRQXBmb0hWNVQrZ244ejdUTWsrSStUU2JlQURubUxCS3lnMHRabW10L0ZKbDV6eWowVmxwc1dzTVM2OVE2bUZJVStqcEhSanpOb2FLMVM1dlQ3ZW1HbUhKSUp0cWlOdXJRN0tkQlBJPC9kczpTaWduYXR1cmVWYWx1ZT4KPGRzOktleUluZm8+PGRzOlg1MDlEYXRhPjxkczpYNTA5Q2VydGlmaWNhdGU+TUlJRWF6Q0NBdE9nQXdJQkFnSVVlN2EwODhDbnI0aXptcm5CRW54NXEzSFRNdll3RFFZSktvWklodmNOQVFFTEJRQXdSVEVMTUFrR0ExVUVCaE1DUjBJeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhJVEFmQmdOVkJBb01HRWx1ZEdWeWJtVjBJRmRwWkdkcGRITWdVSFI1SUV4MFpEQWVGdzB4T1RFeE1UWXhNakUzTVRWYUZ3MHlPVEV4TVRVeE1qRTNNVFZhTUVVeEN6QUpCZ05WQkFZVEFrZENNUk13RVFZRFZRUUlEQXBUYjIxbExWTjBZWFJsTVNFd0h3WURWUVFLREJoSmJuUmxjbTVsZENCWGFXUm5hWFJ6SUZCMGVTQk1kR1F3Z2dHaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQmp3QXdnZ0dLQW9JQmdRRHpMZTlGZmR5cGxUeEhwNFN1UTlnUXRaVDN0K1NEZnZFTDcycHBDZkZadzcrQjVzNUIvVDczYVhwb1EzUzUzcEdJMVJJV0NnZTJpQ1VRMnR6bTI3YVNOSDBpdTlhSlljVVFaL1JJVHFkMGF5eURrczFOQTJQVDNUVzZ0M203S1Y1cmU0UDBOYitZRGV1eUhka3oramNNdHBuOENtQm9UMEgrc2toYTBoaXFJTmtqa1JQaUh2TEhWR3ArdEhVRUEvSTZtTjRhQi9VRXhTVExzNzlOc0xVZnRlcXF4ZTkrdHZkVWFUb3lEUHJoUEZqT05zKzlOS0NreklDNnZjdjdKNkF0dUtHNm5FVCt6Qjl5T1dndEdZUWlmWHFRQTJ5NWRMODFCQjBxNXVNYUJMUzJwcTNhUFBqelUyRjMrRXlzanlTV1RuQ2tmazdDNVNzQ1hSdThRK1U5NXR1bnBOZndmNW9sRTZXYXM0OE5NTStQd1Y3aUNOTVBrTnpsbHE2UENpTStQOERyTVNjenpVWlpRVVN2NmRTd1BDbytZU1ZpbUVNME9nM1hKVGlOaFE1QU5sYUluNjZLdzVnZm9CZnVpWG15SUtpU0R5QWlEWW1GYWY0Mzk1d1d3TGtUUitjdzhXZmphSHN3S1pUb21uMU1SM09Kc1kyVUowZVJCWU0rWVNzQ0F3RUFBYU5UTUZFd0hRWURWUjBPQkJZRUZJbXAyQ1lDR2ZjYjd3OTFIL2NTaFRDa1h3Ui9NQjhHQTFVZEl3UVlNQmFBRkltcDJDWUNHZmNiN3c5MUgvY1NoVENrWHdSL01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dHQkFBK2cvQzd1TDlsbitXK3FCa25MVzgxa29qWWZsZ1BLMUkxTUhJd25NdmwvWlRIWDRkUlhLRHJrN0tjVXExS2pxYWpOVjY2ZjFjYWtwMDNJaWpCaU8wWGkxZ1hVWllMb0NpTkdVeXlwOVhsb2lJeTlYdzJQaVducncwK3laeXZWc3NiZWhYWFlKbDRSaWhCakJXdWw5UjR3TVlMT1VTSkRlMld4Y1VCaEpueHlOUnMrUDB4TFNRWDZCMm42bnhvRGtvNHAwN3M4WktYUWtlaVoyaXdGZFR4elJrR2p0aE1VdjcwNG56c1ZHQlQwRENQdGZTYU81S0paVzFyQ3MzeWlNdGhuQnhxNHFFRE9RSkZJbCsvTEQ3MUtiQjl2WmNXNUp1YXZ6QkZta0tHTnJvLzZHMUk3ZWw0NklSNHdpalR5TkZDWVV1RDlkdGlnbk5tcFd0TjhPVytwdGlML2p0VHlTV3VranlzMHMrdkxuODNDVnZqQjBkSnRWQUlZT2dYRmRJdWlpNjZnY3p3d00vTEdpT0V4Sm4wZFROenNKL0lZaHB4TDRGQkV1UDBwc2tZMG8wYVVsSjJMUzJqK3dTUVRSS3NCZ01qeXJVcmVrbGUyT0RTdFN0bjNlYWJqSXgwL0ZIbHBGcjBqTkltL29NUDdrd2p0VVg0emFOZTQ3UUk0R2c9PTwvZHM6WDUwOUNlcnRpZmljYXRlPjwvZHM6WDUwOURhdGE+PC9kczpLZXlJbmZvPjwvZHM6U2lnbmF0dXJlPjxzYW1sOlN1YmplY3Q+PHNhbWw6TmFtZUlEIFNQTmFtZVF1YWxpZmllcj0iaHR0cDovL2Jvb2tzdGFjay5sb2NhbC9zYW1sMi9tZXRhZGF0YSIgRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6bmFtZWlkLWZvcm1hdDp0cmFuc2llbnQiPl8yYzdhYjg2ZWI4ZjFkMTA2MzQ0M2YyMTljYzU4NjhmZjY2NzA4OTEyZTM8L3NhbWw6TmFtZUlEPjxzYW1sOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDpTdWJqZWN0Q29uZmlybWF0aW9uRGF0YSBOb3RPbk9yQWZ0ZXI9IjIwMTktMTEtMTdUMTc6NTg6MzlaIiBSZWNpcGllbnQ9Imh0dHA6Ly9ib29rc3RhY2subG9jYWwvc2FtbDIvYWNzIiBJblJlc3BvbnNlVG89Ik9ORUxPR0lOXzZhMGY0ZjM5OTMwNDBmMTk4N2ZkMzcwNjhiNTI5NjIyOWFkNTM2MWMiLz48L3NhbWw6U3ViamVjdENvbmZpcm1hdGlvbj48L3NhbWw6U3ViamVjdD48c2FtbDpDb25kaXRpb25zIE5vdEJlZm9yZT0iMjAxOS0xMS0xN1QxNzo1MzowOVoiIE5vdE9uT3JBZnRlcj0iMjAxOS0xMS0xN1QxNzo1ODozOVoiPjxzYW1sOkF1ZGllbmNlUmVzdHJpY3Rpb24+PHNhbWw6QXVkaWVuY2U+aHR0cDovL2Jvb2tzdGFjay5sb2NhbC9zYW1sMi9tZXRhZGF0YTwvc2FtbDpBdWRpZW5jZT48L3NhbWw6QXVkaWVuY2VSZXN0cmljdGlvbj48L3NhbWw6Q29uZGl0aW9ucz48c2FtbDpBdXRoblN0YXRlbWVudCBBdXRobkluc3RhbnQ9IjIwMTktMTEtMTdUMTc6NTM6MzlaIiBTZXNzaW9uTm90T25PckFmdGVyPSIyMDE5LTExLTE4VDAxOjUzOjM5WiIgU2Vzc2lvbkluZGV4PSJfNGZlN2MwZDE1NzJkNjRiMjdmOTMwYWE2ZjIzNmE2ZjQyZTkzMDkwMWNjIj48c2FtbDpBdXRobkNvbnRleHQ+PHNhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+dXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmFjOmNsYXNzZXM6UGFzc3dvcmQ8L3NhbWw6QXV0aG5Db250ZXh0Q2xhc3NSZWY+PC9zYW1sOkF1dGhuQ29udGV4dD48L3NhbWw6QXV0aG5TdGF0ZW1lbnQ+PHNhbWw6QXR0cmlidXRlU3RhdGVtZW50PjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1aWQiIE5hbWVGb3JtYXQ9InVybjpvYXNpczpuYW1lczp0YzpTQU1MOjIuMDphdHRybmFtZS1mb3JtYXQ6YmFzaWMiPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPnVzZXI8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0iZmlyc3RfbmFtZSIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+QmFycnk8L3NhbWw6QXR0cmlidXRlVmFsdWU+PC9zYW1sOkF0dHJpYnV0ZT48c2FtbDpBdHRyaWJ1dGUgTmFtZT0ibGFzdF9uYW1lIiBOYW1lRm9ybWF0PSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXR0cm5hbWUtZm9ybWF0OmJhc2ljIj48c2FtbDpBdHRyaWJ1dGVWYWx1ZSB4c2k6dHlwZT0ieHM6c3RyaW5nIj5TY290dDwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJlbWFpbCIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+dXNlckBleGFtcGxlLmNvbTwvc2FtbDpBdHRyaWJ1dGVWYWx1ZT48L3NhbWw6QXR0cmlidXRlPjxzYW1sOkF0dHJpYnV0ZSBOYW1lPSJ1c2VyX2dyb3VwcyIgTmFtZUZvcm1hdD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOmF0dHJuYW1lLWZvcm1hdDpiYXNpYyI+PHNhbWw6QXR0cmlidXRlVmFsdWUgeHNpOnR5cGU9InhzOnN0cmluZyI+bWVtYmVyPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjxzYW1sOkF0dHJpYnV0ZVZhbHVlIHhzaTp0eXBlPSJ4czpzdHJpbmciPmFkbWluPC9zYW1sOkF0dHJpYnV0ZVZhbHVlPjwvc2FtbDpBdHRyaWJ1dGU+PC9zYW1sOkF0dHJpYnV0ZVN0YXRlbWVudD48L3NhbWw6QXNzZXJ0aW9uPjwvc2FtbHA6UmVzcG9uc2U+';
+
+    protected $sloResponseData = 'fZHRa8IwEMb/lZJ3bdJa04a2MOYYglOY4sNe5JKms9gmpZfC/vxF3ZjC8OXgLvl938ddjtC1vVjZTzu6d429NaiDr641KC5PBRkHIyxgg8JAp1E4JbZPbysRTanoB+ussi25QR4TgKgH11hDguWiIIeawTxOaK1iPYt5XcczHUlJeVRlMklBJjOuM1qDVCTY6wE9WRAv5HHEUS8NOjDOjyjLJoxNGN+xVESpSNgHCRYaXWPAXaijc70IQ2ntyUPqNG2tgjY8Z45CbNFLmt8V7GxBNuuX1eZ1uT7EcZJKAE4TJhXPaMxlVlFffPKKJnXE5ryusoiU+VlMXJIN5Y/feXRn1VR92GkHFTiY9sc+D2+p/HqRrQM34n33bCsd7KEd9eMd4+W32I5KaUQSlleHP9Hwv6uX3w==';
+
+    protected $testCert = 'MIIEazCCAtOgAwIBAgIUe7a088Cnr4izmrnBEnx5q3HTMvYwDQYJKoZIhvcNAQELBQAwRTELMAkGA1UEBhMCR0IxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDAeFw0xOTExMTYxMjE3MTVaFw0yOTExMTUxMjE3MTVaMEUxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDzLe9FfdyplTxHp4SuQ9gQtZT3t+SDfvEL72ppCfFZw7+B5s5B/T73aXpoQ3S53pGI1RIWCge2iCUQ2tzm27aSNH0iu9aJYcUQZ/RITqd0ayyDks1NA2PT3TW6t3m7KV5re4P0Nb+YDeuyHdkz+jcMtpn8CmBoT0H+skha0hiqINkjkRPiHvLHVGp+tHUEA/I6mN4aB/UExSTLs79NsLUfteqqxe9+tvdUaToyDPrhPFjONs+9NKCkzIC6vcv7J6AtuKG6nET+zB9yOWgtGYQifXqQA2y5dL81BB0q5uMaBLS2pq3aPPjzU2F3+EysjySWTnCkfk7C5SsCXRu8Q+U95tunpNfwf5olE6Was48NMM+PwV7iCNMPkNzllq6PCiM+P8DrMSczzUZZQUSv6dSwPCo+YSVimEM0Og3XJTiNhQ5ANlaIn66Kw5gfoBfuiXmyIKiSDyAiDYmFaf4395wWwLkTR+cw8WfjaHswKZTomn1MR3OJsY2UJ0eRBYM+YSsCAwEAAaNTMFEwHQYDVR0OBBYEFImp2CYCGfcb7w91H/cShTCkXwR/MB8GA1UdIwQYMBaAFImp2CYCGfcb7w91H/cShTCkXwR/MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggGBAA+g/C7uL9ln+W+qBknLW81kojYflgPK1I1MHIwnMvl/ZTHX4dRXKDrk7KcUq1KjqajNV66f1cakp03IijBiO0Xi1gXUZYLoCiNGUyyp9XloiIy9Xw2PiWnrw0+yZyvVssbehXXYJl4RihBjBWul9R4wMYLOUSJDe2WxcUBhJnxyNRs+P0xLSQX6B2n6nxoDko4p07s8ZKXQkeiZ2iwFdTxzRkGjthMUv704nzsVGBT0DCPtfSaO5KJZW1rCs3yiMthnBxq4qEDOQJFIl+/LD71KbB9vZcW5JuavzBFmkKGNro/6G1I7el46IR4wijTyNFCYUuD9dtignNmpWtN8OW+ptiL/jtTySWukjys0s+vLn83CVvjB0dJtVAIYOgXFdIuii66gczwwM/LGiOExJn0dTNzsJ/IYhpxL4FBEuP0pskY0o0aUlJ2LS2j+wSQTRKsBgMjyrUrekle2ODStStn3eabjIx0/FHlpFr0jNIm/oMP7kwjtUX4zaNe47QI4Gg==';
+}
index 4aef0ed2658861b2fe8a6f326c12ea131e3888ee..099af2939ddfd932aa1b5b9abb73544d3ce9adda 100644 (file)
@@ -1,6 +1,7 @@
 <?php namespace Tests;
 
 use BookStack\Auth\Permissions\JointPermission;
+use BookStack\Entities\Bookshelf;
 use BookStack\Entities\Page;
 use BookStack\Auth\User;
 use BookStack\Entities\Repos\PageRepo;
@@ -118,4 +119,51 @@ class CommandsTest extends TestCase
         $this->assertTrue(User::where('email', '=', '[email protected]')->first()->hasSystemRole('admin'), 'User has admin role as expected');
         $this->assertTrue(\Auth::attempt(['email' => '[email protected]', 'password' => 'testing-4']), 'Password stored as expected');
     }
+
+    public function test_copy_shelf_permissions_command_shows_error_when_no_required_option_given()
+    {
+        $this->artisan('bookstack:copy-shelf-permissions')
+            ->expectsOutput('Either a --slug or --all option must be provided.')
+            ->assertExitCode(0);
+    }
+
+    public function test_copy_shelf_permissions_command_using_slug()
+    {
+        $shelf = Bookshelf::first();
+        $child = $shelf->books()->first();
+        $editorRole = $this->getEditor()->roles()->first();
+        $this->assertFalse(boolval($child->restricted), "Child book should not be restricted by default");
+        $this->assertTrue($child->permissions()->count() === 0, "Child book should have no permissions by default");
+
+        $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+        $this->artisan('bookstack:copy-shelf-permissions', [
+            '--slug' => $shelf->slug,
+        ]);
+        $child = $shelf->books()->first();
+
+        $this->assertTrue(boolval($child->restricted), "Child book should now be restricted");
+        $this->assertTrue($child->permissions()->count() === 2, "Child book should have copied permissions");
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'view', 'role_id' => $editorRole->id]);
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
+    }
+
+    public function test_copy_shelf_permissions_command_using_all()
+    {
+        $shelf = Bookshelf::query()->first();
+        Bookshelf::query()->where('id', '!=', $shelf->id)->delete();
+        $child = $shelf->books()->first();
+        $editorRole = $this->getEditor()->roles()->first();
+        $this->assertFalse(boolval($child->restricted), "Child book should not be restricted by default");
+        $this->assertTrue($child->permissions()->count() === 0, "Child book should have no permissions by default");
+
+        $this->setEntityRestrictions($shelf, ['view', 'update'], [$editorRole]);
+        $this->artisan('bookstack:copy-shelf-permissions --all')
+            ->expectsQuestion('Permission settings for all shelves will be cascaded. Books assigned to multiple shelves will receive only the permissions of it\'s last processed shelf. Are you sure you want to proceed?', 'y');
+        $child = $shelf->books()->first();
+
+        $this->assertTrue(boolval($child->restricted), "Child book should now be restricted");
+        $this->assertTrue($child->permissions()->count() === 2, "Child book should have copied permissions");
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'view', 'role_id' => $editorRole->id]);
+        $this->assertDatabaseHas('entity_permissions', ['restrictable_id' => $child->id, 'action' => 'update', 'role_id' => $editorRole->id]);
+    }
 }
index 3d12ed7495943dcc75ef979d87d845a57c41ee47..97684ea4d66fc174e82c8c8b81b55a35488193d7 100644 (file)
@@ -315,4 +315,14 @@ class EntityTest extends BrowserKitTest
             ->seePageIs($book->getUrl());
     }
 
+    public function test_page_within_chapter_deletion_returns_to_chapter()
+    {
+        $chapter = Chapter::query()->first();
+        $page = $chapter->pages()->first();
+
+        $this->asEditor()->visit($page->getUrl('/delete'))
+            ->submitForm('Confirm')
+            ->seePageIs($chapter->getUrl());
+    }
+
 }
index 371cffc0f3bd1be95a263c2bffd4c5f4277bb8f2..2ef72fb0aef8999403553d00819ae59ea9a2acf4 100644 (file)
@@ -637,14 +637,14 @@ class RolesTest extends BrowserKitTest
             $ownPage->getUrl() => 'Delete'
         ]);
 
-        $bookUrl = $ownPage->book->getUrl();
+        $parent = $ownPage->chapter ?? $ownPage->book;
         $this->visit($otherPage->getUrl())
             ->dontSeeInElement('.action-buttons', 'Delete')
             ->visit($otherPage->getUrl() . '/delete')
             ->seePageIs('/');
         $this->visit($ownPage->getUrl())->visit($ownPage->getUrl() . '/delete')
             ->press('Confirm')
-            ->seePageIs($bookUrl)
+            ->seePageIs($parent->getUrl())
             ->dontSeeInElement('.book-content', $ownPage->name);
     }
 
@@ -658,10 +658,10 @@ class RolesTest extends BrowserKitTest
             $otherPage->getUrl() => 'Delete'
         ]);
 
-        $bookUrl = $otherPage->book->getUrl();
+        $parent = $otherPage->chapter ?? $otherPage->book;
         $this->visit($otherPage->getUrl())->visit($otherPage->getUrl() . '/delete')
             ->press('Confirm')
-            ->seePageIs($bookUrl)
+            ->seePageIs($parent->getUrl())
             ->dontSeeInElement('.book-content', $otherPage->name);
     }
 
index 0615a95ce52641cff442ce913d59bb65f812d2b3..3f6c021a7f0f86c74ad972c4166b0e64b485518a 100644 (file)
@@ -36,6 +36,30 @@ class ImageTest extends TestCase
         ]);
     }
 
+    public function test_image_display_thumbnail_generation_does_not_increase_image_size()
+    {
+        $page = Page::first();
+        $admin = $this->getAdmin();
+        $this->actingAs($admin);
+
+        $originalFile = $this->getTestImageFilePath('compressed.png');
+        $originalFileSize = filesize($originalFile);
+        $imgDetails = $this->uploadGalleryImage($page, 'compressed.png');
+        $relPath = $imgDetails['path'];
+
+        $this->assertTrue(file_exists(public_path($relPath)), 'Uploaded image found at path: '. public_path($relPath));
+        $displayImage = $imgDetails['response']->thumbs->display;
+
+        $displayImageRelPath = implode('/', array_slice(explode('/', $displayImage), 3));
+        $displayImagePath = public_path($displayImageRelPath);
+        $displayFileSize = filesize($displayImagePath);
+
+        $this->deleteImage($relPath);
+        $this->deleteImage($displayImageRelPath);
+
+        $this->assertEquals($originalFileSize, $displayFileSize, 'Display thumbnail generation should not increase image size');
+    }
+
     public function test_image_edit()
     {
         $editor = $this->getEditor();
index aa5ffe4c78250fb41f3a96c1319970a06eae3344..b24b483d945ec1e7f510ac621bdd6244fdec49a4 100644 (file)
@@ -10,9 +10,13 @@ trait UsesImages
      * Get the path to our basic test image.
      * @return string
      */
-    protected function getTestImageFilePath()
+    protected function getTestImageFilePath(?string $fileName = null)
     {
-        return base_path('tests/test-data/test-image.png');
+        if (is_null($fileName)) {
+            $fileName = 'test-image.png';
+        }
+
+        return base_path('tests/test-data/' . $fileName);
     }
 
     /**
@@ -20,9 +24,9 @@ trait UsesImages
      * @param $fileName
      * @return UploadedFile
      */
-    protected function getTestImage($fileName)
+    protected function getTestImage($fileName, ?string $testDataFileName = null)
     {
-        return new UploadedFile($this->getTestImageFilePath(), $fileName, 'image/png', 5238, null, true);
+        return new UploadedFile($this->getTestImageFilePath($testDataFileName), $fileName, 'image/png', 5238, null, true);
     }
 
     /**
@@ -52,9 +56,9 @@ trait UsesImages
      * @param string $contentType
      * @return \Illuminate\Foundation\Testing\TestResponse
      */
-    protected function uploadImage($name, $uploadedTo = 0, $contentType = 'image/png')
+    protected function uploadImage($name, $uploadedTo = 0, $contentType = 'image/png', ?string $testDataFileName = null)
     {
-        $file = $this->getTestImage($name);
+        $file = $this->getTestImage($name, $testDataFileName);
         return $this->withHeader('Content-Type', $contentType)
             ->call('POST', '/images/gallery', ['uploaded_to' => $uploadedTo], [], ['file' => $file], []);
     }
@@ -66,22 +70,23 @@ trait UsesImages
      * @param Page|null $page
      * @return array
      */
-    protected function uploadGalleryImage(Page $page = null)
+    protected function uploadGalleryImage(Page $page = null, ?string $testDataFileName = null)
     {
         if ($page === null) {
             $page = Page::query()->first();
         }
 
-        $imageName = 'first-image.png';
+        $imageName = $testDataFileName ?? 'first-image.png';
         $relPath = $this->getTestImagePath('gallery', $imageName);
         $this->deleteImage($relPath);
 
-        $upload = $this->uploadImage($imageName, $page->id);
+        $upload = $this->uploadImage($imageName, $page->id, 'image/png', $testDataFileName);
         $upload->assertStatus(200);
         return [
             'name' => $imageName,
             'path' => $relPath,
-            'page' => $page
+            'page' => $page,
+            'response' => json_decode($upload->getContent()),
         ];
     }
 
diff --git a/tests/test-data/compressed.png b/tests/test-data/compressed.png
new file mode 100644 (file)
index 0000000..e42ef58
Binary files /dev/null and b/tests/test-data/compressed.png differ