diff --git a/.env.example b/.env.example index 6e015335e..1005ad208 100644 --- a/.env.example +++ b/.env.example @@ -60,8 +60,13 @@ GITLAB_BASE_URI=false DISCORD_APP_ID=false DISCORD_APP_SECRET=false -# External services such as Gravatar and Draw.IO + +# Disable default services such as Gravatar and Draw.IO DISABLE_EXTERNAL_SERVICES=false +# Use custom avatar service, Sets fetch URL +# Possible placeholders: ${hash} ${size} ${email} +# If set, Avatars will be fetched regardless of DISABLE_EXTERNAL_SERVICES option. +# AVATAR_URL=https://seccdn.libravatar.org/avatar/${hash}?s=${size}&d=identicon # LDAP Settings LDAP_SERVER=false @@ -77,6 +82,8 @@ LDAP_GROUP_ATTRIBUTE="memberOf" # Would you like to remove users from roles on BookStack if they do not match on LDAP # If false, the ldap groups-roles sync will only add users to roles LDAP_REMOVE_FROM_GROUPS=false +# Set this option to disable LDAPS Certificate Verification +LDAP_TLS_INSECURE=false # Mail settings MAIL_DRIVER=smtp diff --git a/app/Auth/Access/Ldap.php b/app/Auth/Access/Ldap.php index 468c37626..843a2f204 100644 --- a/app/Auth/Access/Ldap.php +++ b/app/Auth/Access/Ldap.php @@ -92,4 +92,27 @@ class Ldap { return ldap_bind($ldapConnection, $bindRdn, $bindPassword); } + + /** + * Explode a LDAP dn string into an array of components. + * @param string $dn + * @param int $withAttrib + * @return array + */ + public function explodeDn(string $dn, int $withAttrib) + { + return ldap_explode_dn($dn, $withAttrib); + } + + /** + * Escape a string for use in an LDAP filter. + * @param string $value + * @param string $ignore + * @param int $flags + * @return string + */ + public function escape(string $value, string $ignore = "", int $flags = 0) + { + return ldap_escape($value, $ignore, $flags); + } } diff --git a/app/Auth/Access/LdapService.php b/app/Auth/Access/LdapService.php index d3a177f8e..1e95ac513 100644 --- a/app/Auth/Access/LdapService.php +++ b/app/Auth/Access/LdapService.php @@ -107,6 +107,7 @@ class LdapService if ($ldapUser === null) { return false; } + if ($ldapUser['uid'] !== $user->external_auth_id) { return false; } @@ -169,6 +170,16 @@ class LdapService } $hostName = $ldapServer[0] . ($hasProtocol?':':'') . $ldapServer[1]; $defaultPort = $ldapServer[0] === 'ldaps' ? 636 : 389; + + /* + * Check if TLS_INSECURE is set. The handle is set to NULL due to the nature of + * the LDAP_OPT_X_TLS_REQUIRE_CERT option. It can only be set globally and not + * per handle. + */ + if($this->config['tls_insecure']) { + $this->ldap->setOption(NULL, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER); + } + $ldapConnection = $this->ldap->connect($hostName, count($ldapServer) > 2 ? intval($ldapServer[2]) : $defaultPort); if ($ldapConnection === false) { @@ -195,7 +206,7 @@ class LdapService $newAttrs = []; foreach ($attrs as $key => $attrText) { $newKey = '${' . $key . '}'; - $newAttrs[$newKey] = $attrText; + $newAttrs[$newKey] = $this->ldap->escape($attrText); } return strtr($filterString, $newAttrs); } @@ -265,7 +276,8 @@ class LdapService $baseDn = $this->config['base_dn']; $groupsAttr = strtolower($this->config['group_attribute']); - $groups = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, 'CN='.$groupName, [$groupsAttr]); + $groupFilter = 'CN=' . $this->ldap->escape($groupName); + $groups = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $groupFilter, [$groupsAttr]); if ($groups['count'] === 0) { return []; } @@ -277,23 +289,26 @@ class LdapService /** * Filter out LDAP CN and DN language in a ldap search return * Gets the base CN (common name) of the string - * @param string $ldapSearchReturn + * @param array $userGroupSearchResponse * @return array */ - protected function groupFilter($ldapSearchReturn) + protected function groupFilter(array $userGroupSearchResponse) { $groupsAttr = strtolower($this->config['group_attribute']); $ldapGroups = []; $count = 0; - if (isset($ldapSearchReturn[$groupsAttr]['count'])) { - $count = (int) $ldapSearchReturn[$groupsAttr]['count']; + + if (isset($userGroupSearchResponse[$groupsAttr]['count'])) { + $count = (int) $userGroupSearchResponse[$groupsAttr]['count']; } + for ($i=0; $i<$count; $i++) { - $dnComponents = ldap_explode_dn($ldapSearchReturn[$groupsAttr][$i], 1); + $dnComponents = $this->ldap->explodeDn($userGroupSearchResponse[$groupsAttr][$i], 1); if (!in_array($dnComponents[0], $ldapGroups)) { $ldapGroups[] = $dnComponents[0]; } } + return $ldapGroups; } diff --git a/app/Auth/UserRepo.php b/app/Auth/UserRepo.php index 7c88badb8..31b91108d 100644 --- a/app/Auth/UserRepo.php +++ b/app/Auth/UserRepo.php @@ -3,6 +3,7 @@ use Activity; use BookStack\Entities\Repos\EntityRepo; use BookStack\Exceptions\NotFoundException; +use BookStack\Exceptions\UserUpdateException; use BookStack\Uploads\Image; use Exception; use Images; @@ -42,7 +43,7 @@ class UserRepo */ public function getById($id) { - return $this->user->findOrFail($id); + return $this->user->newQuery()->findOrFail($id); } /** @@ -85,9 +86,7 @@ class UserRepo { $user = $this->create($data, $verifyEmail); $this->attachDefaultRole($user); - - // Get avatar from gravatar and save - $this->downloadGravatarToUserAvatar($user); + $this->downloadAndAssignUserAvatar($user); return $user; } @@ -137,6 +136,40 @@ class UserRepo return true; } + /** + * Set the assigned user roles via an array of role IDs. + * @param User $user + * @param array $roles + * @throws UserUpdateException + */ + public function setUserRoles(User $user, array $roles) + { + if ($this->demotingLastAdmin($user, $roles)) { + throw new UserUpdateException(trans('errors.role_cannot_remove_only_admin'), $user->getEditUrl()); + } + + $user->roles()->sync($roles); + } + + /** + * Check if the given user is the last admin and their new roles no longer + * contains the admin role. + * @param User $user + * @param array $newRoles + * @return bool + */ + protected function demotingLastAdmin(User $user, array $newRoles) : bool + { + if ($this->isOnlyAdmin($user)) { + $adminRole = $this->role->getSystemRole('admin'); + if (!in_array(strval($adminRole->id), $newRoles)) { + return true; + } + } + + return false; + } + /** * Create a new basic instance of user. * @param array $data @@ -145,7 +178,6 @@ class UserRepo */ public function create(array $data, $verifyEmail = false) { - return $this->user->forceCreate([ 'name' => $data['name'], 'email' => $data['email'], @@ -238,25 +270,24 @@ class UserRepo } /** - * Get a gravatar image for a user and set it as their avatar. - * Does not run if gravatar disabled in config. + * Get an avatar image for a user and set it as their avatar. + * Returns early if avatars disabled or not set in config. * @param User $user * @return bool */ - public function downloadGravatarToUserAvatar(User $user) + public function downloadAndAssignUserAvatar(User $user) { - // Get avatar from gravatar and save - if (!config('services.gravatar')) { + if (!Images::avatarFetchEnabled()) { return false; } try { - $avatar = Images::saveUserGravatar($user); + $avatar = Images::saveUserAvatar($user); $user->avatar()->associate($avatar); $user->save(); return true; } catch (Exception $e) { - \Log::error('Failed to save user gravatar image'); + \Log::error('Failed to save user avatar image'); return false; } } diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php index 6bfc54469..90c1ddb1c 100644 --- a/app/Console/Commands/CreateAdmin.php +++ b/app/Console/Commands/CreateAdmin.php @@ -76,7 +76,7 @@ class CreateAdmin extends Command $user = $this->userRepo->create(['email' => $email, 'name' => $name, 'password' => $password]); $this->userRepo->attachSystemRole($user, 'admin'); - $this->userRepo->downloadGravatarToUserAvatar($user); + $this->userRepo->downloadAndAssignUserAvatar($user); $user->email_confirmed = true; $user->save(); diff --git a/app/Exceptions/HttpFetchException.php b/app/Exceptions/HttpFetchException.php new file mode 100644 index 000000000..48e30e1e6 --- /dev/null +++ b/app/Exceptions/HttpFetchException.php @@ -0,0 +1,5 @@ +message = $message; $this->redirectLocation = $redirectLocation; diff --git a/app/Exceptions/UserUpdateException.php b/app/Exceptions/UserUpdateException.php new file mode 100644 index 000000000..eb41dece6 --- /dev/null +++ b/app/Exceptions/UserUpdateException.php @@ -0,0 +1,3 @@ +getLocale(); $cacheKey = 'GLOBAL_TRANSLATIONS_' . $locale; + if (cache()->has($cacheKey) && config('app.env') !== 'development') { $resp = cache($cacheKey); } else { @@ -89,15 +90,6 @@ class HomeController extends Controller 'entities' => trans('entities'), 'errors' => trans('errors') ]; - if ($locale !== 'en') { - $enTrans = [ - 'common' => trans('common', [], 'en'), - 'components' => trans('components', [], 'en'), - 'entities' => trans('entities', [], 'en'), - 'errors' => trans('errors', [], 'en') - ]; - $translations = array_replace_recursive($enTrans, $translations); - } $resp = 'window.translations = ' . json_encode($translations); cache()->put($cacheKey, $resp, 120); } diff --git a/app/Http/Controllers/UserController.php b/app/Http/Controllers/UserController.php index 5f5c8365e..cc5ada3f2 100644 --- a/app/Http/Controllers/UserController.php +++ b/app/Http/Controllers/UserController.php @@ -3,6 +3,7 @@ use BookStack\Auth\Access\SocialAuthService; use BookStack\Auth\User; use BookStack\Auth\UserRepo; +use BookStack\Exceptions\UserUpdateException; use Illuminate\Http\Request; use Illuminate\Http\Response; @@ -15,7 +16,7 @@ class UserController extends Controller /** * UserController constructor. * @param User $user - * @param \BookStack\Auth\UserRepo $userRepo + * @param UserRepo $userRepo */ public function __construct(User $user, UserRepo $userRepo) { @@ -59,6 +60,7 @@ class UserController extends Controller * Store a newly created user in storage. * @param Request $request * @return Response + * @throws UserUpdateException */ public function store(Request $request) { @@ -89,10 +91,10 @@ class UserController extends Controller if ($request->filled('roles')) { $roles = $request->get('roles'); - $user->roles()->sync($roles); + $this->userRepo->setUserRoles($user, $roles); } - $this->userRepo->downloadGravatarToUserAvatar($user); + $this->userRepo->downloadAndAssignUserAvatar($user); return redirect('/settings/users'); } @@ -122,8 +124,9 @@ class UserController extends Controller /** * Update the specified user in storage. * @param Request $request - * @param int $id + * @param int $id * @return Response + * @throws UserUpdateException */ public function update(Request $request, $id) { @@ -140,13 +143,13 @@ class UserController extends Controller 'setting' => 'array' ]); - $user = $this->user->findOrFail($id); + $user = $this->userRepo->getById($id); $user->fill($request->all()); // Role updates if (userCan('users-manage') && $request->filled('roles')) { $roles = $request->get('roles'); - $user->roles()->sync($roles); + $this->userRepo->setUserRoles($user, $roles); } // Password updates @@ -185,7 +188,7 @@ class UserController extends Controller return $this->currentUser->id == $id; }); - $user = $this->user->findOrFail($id); + $user = $this->userRepo->getById($id); $this->setPageTitle(trans('settings.users_delete_named', ['userName' => $user->name])); return view('users/delete', ['user' => $user]); } @@ -194,6 +197,7 @@ class UserController extends Controller * Remove the specified user from storage. * @param int $id * @return Response + * @throws \Exception */ public function destroy($id) { @@ -279,7 +283,7 @@ class UserController extends Controller $viewType = 'list'; } - $user = $this->user->findOrFail($id); + $user = $this->userRepo->getById($id); setting()->putUser($user, 'bookshelves_view_type', $viewType); return redirect()->back(302, [], "/settings/users/$id"); diff --git a/app/Notifications/ConfirmEmail.php b/app/Notifications/ConfirmEmail.php index f3797e6e2..7ecadc298 100644 --- a/app/Notifications/ConfirmEmail.php +++ b/app/Notifications/ConfirmEmail.php @@ -1,17 +1,7 @@ -token = $token; } - /** - * Get the notification's delivery channels. - * - * @param mixed $notifiable - * @return array - */ - public function via($notifiable) - { - return ['mail']; - } - /** * Get the mail representation of the notification. * @@ -43,10 +22,10 @@ class ConfirmEmail extends Notification implements ShouldQueue public function toMail($notifiable) { $appName = ['appName' => setting('app-name')]; - return (new MailMessage) - ->subject(trans('auth.email_confirm_subject', $appName)) - ->greeting(trans('auth.email_confirm_greeting', $appName)) - ->line(trans('auth.email_confirm_text')) - ->action(trans('auth.email_confirm_action'), baseUrl('/register/confirm/' . $this->token)); + return $this->newMailMessage() + ->subject(trans('auth.email_confirm_subject', $appName)) + ->greeting(trans('auth.email_confirm_greeting', $appName)) + ->line(trans('auth.email_confirm_text')) + ->action(trans('auth.email_confirm_action'), baseUrl('/register/confirm/' . $this->token)); } } diff --git a/app/Notifications/MailNotification.php b/app/Notifications/MailNotification.php new file mode 100644 index 000000000..413ac6d73 --- /dev/null +++ b/app/Notifications/MailNotification.php @@ -0,0 +1,35 @@ +view([ + 'html' => 'vendor.notifications.email', + 'text' => 'vendor.notifications.email-plain' + ]); + } + +} \ No newline at end of file diff --git a/app/Notifications/ResetPassword.php b/app/Notifications/ResetPassword.php index 86e93228f..282aa335a 100644 --- a/app/Notifications/ResetPassword.php +++ b/app/Notifications/ResetPassword.php @@ -1,11 +1,7 @@ -token = $token; } - /** - * Get the notification's channels. - * - * @param mixed $notifiable - * @return array|string - */ - public function via($notifiable) - { - return ['mail']; - } - /** * Build the mail representation of the notification. * @@ -42,7 +27,7 @@ class ResetPassword extends Notification */ public function toMail() { - return (new MailMessage) + return $this->newMailMessage() ->subject(trans('auth.email_reset_subject', ['appName' => setting('app-name')])) ->line(trans('auth.email_reset_text')) ->action(trans('auth.reset_password'), baseUrl('password/reset/' . $this->token)) diff --git a/app/Providers/CustomFacadeProvider.php b/app/Providers/CustomFacadeProvider.php index 98b242d21..5508ee9cd 100644 --- a/app/Providers/CustomFacadeProvider.php +++ b/app/Providers/CustomFacadeProvider.php @@ -9,6 +9,7 @@ use BookStack\Actions\ViewService; use BookStack\Auth\Permissions\PermissionService; use BookStack\Settings\Setting; use BookStack\Settings\SettingService; +use BookStack\Uploads\HttpFetcher; use BookStack\Uploads\Image; use BookStack\Uploads\ImageService; use Illuminate\Contracts\Cache\Repository; @@ -61,7 +62,8 @@ class CustomFacadeProvider extends ServiceProvider $this->app->make(Image::class), $this->app->make(ImageManager::class), $this->app->make(Factory::class), - $this->app->make(Repository::class) + $this->app->make(Repository::class), + $this->app->make(HttpFetcher::class) ); }); } diff --git a/app/Providers/TranslationServiceProvider.php b/app/Providers/TranslationServiceProvider.php new file mode 100644 index 000000000..0e628c7da --- /dev/null +++ b/app/Providers/TranslationServiceProvider.php @@ -0,0 +1,32 @@ +registerLoader(); + + $this->app->singleton('translator', function ($app) { + $loader = $app['translation.loader']; + + // When registering the translator component, we'll need to set the default + // locale as well as the fallback locale. So, we'll grab the application + // configuration so we can easily get both of these values from there. + $locale = $app['config']['app.locale']; + + $trans = new Translator($loader, $locale); + + $trans->setFallback($app['config']['app.fallback_locale']); + + return $trans; + }); + } +} \ No newline at end of file diff --git a/app/Translation/Translator.php b/app/Translation/Translator.php new file mode 100644 index 000000000..2edfecf73 --- /dev/null +++ b/app/Translation/Translator.php @@ -0,0 +1,74 @@ + 'de', + ]; + + /** + * Get the translation for a given key. + * + * @param string $key + * @param array $replace + * @param string $locale + * @return string|array|null + */ + public function trans($key, array $replace = [], $locale = null) + { + $translation = $this->get($key, $replace, $locale); + + if (is_array($translation)) { + $translation = $this->mergeBackupTranslations($translation, $key, $locale); + } + + return $translation; + } + + /** + * Merge the fallback translations, and base translations if existing, + * into the provided core key => value array of translations content. + * @param array $translationArray + * @param string $key + * @param null $locale + * @return array + */ + protected function mergeBackupTranslations(array $translationArray, string $key, $locale = null) + { + $fallback = $this->get($key, [], $this->fallback); + $baseLocale = $this->getBaseLocale($locale ?? $this->locale); + $baseTranslations = $baseLocale ? $this->get($key, [], $baseLocale) : []; + + return array_replace_recursive($fallback, $baseTranslations, $translationArray); + } + + /** + * Get the array of locales to be checked. + * + * @param string|null $locale + * @return array + */ + protected function localeArray($locale) + { + $primaryLocale = $locale ?: $this->locale; + return array_filter([$primaryLocale, $this->getBaseLocale($primaryLocale), $this->fallback]); + } + + /** + * Get the locale to extend for the given locale. + * + * @param string $locale + * @return string|null + */ + protected function getBaseLocale($locale) + { + return $this->baseLocaleMap[$locale] ?? null; + } + +} \ No newline at end of file diff --git a/app/Uploads/HttpFetcher.php b/app/Uploads/HttpFetcher.php new file mode 100644 index 000000000..3ebe17eee --- /dev/null +++ b/app/Uploads/HttpFetcher.php @@ -0,0 +1,34 @@ + $uri, + CURLOPT_RETURNTRANSFER => 1, + CURLOPT_CONNECTTIMEOUT => 5 + ]); + + $data = curl_exec($ch); + $err = curl_error($ch); + curl_close($ch); + + if ($err) { + throw new HttpFetchException($err); + } + + return $data; + } + +} \ No newline at end of file diff --git a/app/Uploads/ImageService.php b/app/Uploads/ImageService.php index f109db600..1dd8b713d 100644 --- a/app/Uploads/ImageService.php +++ b/app/Uploads/ImageService.php @@ -1,6 +1,7 @@ image = $image; $this->imageTool = $imageTool; $this->cache = $cache; + $this->http = $http; parent::__construct($fileSystem); } @@ -95,8 +99,9 @@ class ImageService extends UploadService private function saveNewFromUrl($url, $type, $imageName = false) { $imageName = $imageName ? $imageName : basename($url); - $imageData = file_get_contents($url); - if ($imageData === false) { + try { + $imageData = $this->http->fetch($url); + } catch (HttpFetchException $exception) { throw new \Exception(trans('errors.cannot_get_image_from_url', ['url' => $url])); } return $this->saveNew($imageName, $imageData, $type); @@ -279,24 +284,57 @@ class ImageService extends UploadService } /** - * Save a gravatar image and set a the profile image for a user. + * Save an avatar image from an external service. * @param \BookStack\Auth\User $user * @param int $size - * @return mixed + * @return Image * @throws Exception */ - public function saveUserGravatar(User $user, $size = 500) + public function saveUserAvatar(User $user, $size = 500) { - $emailHash = md5(strtolower(trim($user->email))); - $url = 'https://www.gravatar.com/avatar/' . $emailHash . '?s=' . $size . '&d=identicon'; - $imageName = str_replace(' ', '-', $user->name . '-gravatar.png'); - $image = $this->saveNewFromUrl($url, 'user', $imageName); + $avatarUrl = $this->getAvatarUrl(); + $email = strtolower(trim($user->email)); + + $replacements = [ + '${hash}' => md5($email), + '${size}' => $size, + '${email}' => urlencode($email), + ]; + + $userAvatarUrl = strtr($avatarUrl, $replacements); + $imageName = str_replace(' ', '-', $user->name . '-avatar.png'); + $image = $this->saveNewFromUrl($userAvatarUrl, 'user', $imageName); $image->created_by = $user->id; $image->updated_by = $user->id; $image->save(); + return $image; } + /** + * Check if fetching external avatars is enabled. + * @return bool + */ + public function avatarFetchEnabled() + { + $fetchUrl = $this->getAvatarUrl(); + return is_string($fetchUrl) && strpos($fetchUrl, 'http') === 0; + } + + /** + * Get the URL to fetch avatars from. + * @return string|mixed + */ + protected function getAvatarUrl() + { + $url = trim(config('services.avatar_url')); + + if (empty($url) && !config('services.disable_services')) { + $url = 'https://www.gravatar.com/avatar/${hash}?s=${size}&d=identicon'; + } + + return $url; + } /** * Delete gallery and drawings that are not within HTML content of pages or page revisions. @@ -365,14 +403,7 @@ class ImageService extends UploadService } } else { try { - $ch = curl_init(); - curl_setopt_array($ch, [CURLOPT_URL => $uri, CURLOPT_RETURNTRANSFER => 1, CURLOPT_CONNECTTIMEOUT => 5]); - $imageData = curl_exec($ch); - $err = curl_error($ch); - curl_close($ch); - if ($err) { - throw new \Exception("Image fetch failed, Received error: " . $err); - } + $imageData = $this->http->fetch($uri); } catch (\Exception $e) { } } diff --git a/composer.json b/composer.json index a34c65091..48b977e23 100644 --- a/composer.json +++ b/composer.json @@ -5,9 +5,14 @@ "license": "MIT", "type": "project", "require": { - "php": ">=7.0.0", + "php": ">=7.0.5", + "ext-json": "*", "ext-tidy": "*", "ext-dom": "*", + "ext-xml": "*", + "ext-mbstring": "*", + "ext-gd": "*", + "ext-curl": "*", "laravel/framework": "~5.5.44", "fideloper/proxy": "~3.3", "intervention/image": "^2.4", @@ -82,7 +87,7 @@ "optimize-autoloader": true, "preferred-install": "dist", "platform": { - "php": "7.0" + "php": "7.0.5" } } } diff --git a/composer.lock b/composer.lock index 8b7657da2..c524c0999 100644 --- a/composer.lock +++ b/composer.lock @@ -4,20 +4,20 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "4a15ede09baa575d7accd7a4f66067fd", + "content-hash": "06219a5c2419ca23ec2924eb31f4ed16", "packages": [ { "name": "aws/aws-sdk-php", - "version": "3.70.3", + "version": "3.82.3", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "8278052a097a4ebe2b798fab7e2e3c907bc01a47" + "reference": "a0353c24b18d2ba0f5bb7ca8a478b4ce0b8153f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/8278052a097a4ebe2b798fab7e2e3c907bc01a47", - "reference": "8278052a097a4ebe2b798fab7e2e3c907bc01a47", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/a0353c24b18d2ba0f5bb7ca8a478b4ce0b8153f7", + "reference": "a0353c24b18d2ba0f5bb7ca8a478b4ce0b8153f7", "shasum": "" }, "require": { @@ -87,7 +87,7 @@ "s3", "sdk" ], - "time": "2018-11-02T20:04:36+00:00" + "time": "2018-12-21T22:21:50+00:00" }, { "name": "barryvdh/laravel-dompdf", @@ -729,30 +729,34 @@ }, { "name": "dompdf/dompdf", - "version": "v0.8.2", + "version": "v0.8.3", "source": { "type": "git", "url": "https://github.com/dompdf/dompdf.git", - "reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6" + "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/dompdf/dompdf/zipball/5113accd9ae5d466077cce5208dcf3fb871bf8f6", - "reference": "5113accd9ae5d466077cce5208dcf3fb871bf8f6", + "url": "https://api.github.com/repos/dompdf/dompdf/zipball/75f13c700009be21a1965dc2c5b68a8708c22ba2", + "reference": "75f13c700009be21a1965dc2c5b68a8708c22ba2", "shasum": "" }, "require": { "ext-dom": "*", - "ext-gd": "*", "ext-mbstring": "*", "phenx/php-font-lib": "0.5.*", "phenx/php-svg-lib": "0.3.*", "php": ">=5.4.0" }, "require-dev": { - "phpunit/phpunit": "4.8.*", + "phpunit/phpunit": "^4.8|^5.5|^6.5", "squizlabs/php_codesniffer": "2.*" }, + "suggest": { + "ext-gd": "Needed to process images", + "ext-gmagick": "Improves image processing performance", + "ext-imagick": "Improves image processing performance" + }, "type": "library", "extra": { "branch-alias": { @@ -787,20 +791,20 @@ ], "description": "DOMPDF is a CSS 2.1 compliant HTML to PDF converter", "homepage": "https://github.com/dompdf/dompdf", - "time": "2017-11-26T14:49:08+00:00" + "time": "2018-12-14T02:40:31+00:00" }, { "name": "egulias/email-validator", - "version": "2.1.6", + "version": "2.1.7", "source": { "type": "git", "url": "https://github.com/egulias/EmailValidator.git", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786" + "reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/0578b32b30b22de3e8664f797cf846fc9246f786", - "reference": "0578b32b30b22de3e8664f797cf846fc9246f786", + "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/709f21f92707308cdf8f9bcfa1af4cb26586521e", + "reference": "709f21f92707308cdf8f9bcfa1af4cb26586521e", "shasum": "" }, "require": { @@ -844,7 +848,7 @@ "validation", "validator" ], - "time": "2018-09-25T20:47:26+00:00" + "time": "2018-12-04T22:38:24+00:00" }, { "name": "erusev/parsedown", @@ -1116,32 +1120,33 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "1.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "9f83dded91781a01c63574e387eaa769be769115" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115", + "reference": "9f83dded91781a01c63574e387eaa769be769115", "shasum": "" }, "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.5-dev" } }, "autoload": { @@ -1171,13 +1176,14 @@ "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2018-12-04T20:46:45+00:00" }, { "name": "intervention/image", @@ -1514,16 +1520,16 @@ }, { "name": "league/flysystem", - "version": "1.0.48", + "version": "1.0.49", "source": { "type": "git", "url": "https://github.com/thephpleague/flysystem.git", - "reference": "a6ded5b2f6055e2db97b4b859fdfca2b952b78aa" + "reference": "a63cc83d8a931b271be45148fa39ba7156782ffd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a6ded5b2f6055e2db97b4b859fdfca2b952b78aa", - "reference": "a6ded5b2f6055e2db97b4b859fdfca2b952b78aa", + "url": "https://api.github.com/repos/thephpleague/flysystem/zipball/a63cc83d8a931b271be45148fa39ba7156782ffd", + "reference": "a63cc83d8a931b271be45148fa39ba7156782ffd", "shasum": "" }, "require": { @@ -1594,7 +1600,7 @@ "sftp", "storage" ], - "time": "2018-10-15T13:53:10+00:00" + "time": "2018-11-23T23:41:29+00:00" }, { "name": "league/flysystem-aws-s3-v3", @@ -1708,16 +1714,16 @@ }, { "name": "monolog/monolog", - "version": "1.23.0", + "version": "1.24.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4" + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/fd8c787753b3a2ad11bc60c063cff1358a32a3b4", - "reference": "fd8c787753b3a2ad11bc60c063cff1358a32a3b4", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", + "reference": "bfc9ebb28f97e7a24c45bdc3f0ff482e47bb0266", "shasum": "" }, "require": { @@ -1782,7 +1788,7 @@ "logging", "psr-3" ], - "time": "2017-06-19T01:22:40+00:00" + "time": "2018-11-05T09:00:11+00:00" }, { "name": "mtdowling/cron-expression", @@ -1885,16 +1891,16 @@ }, { "name": "nesbot/carbon", - "version": "1.34.0", + "version": "1.36.1", "source": { "type": "git", "url": "https://github.com/briannesbitt/Carbon.git", - "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33" + "reference": "63da8cdf89d7a5efe43aabc794365f6e7b7b8983" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", - "reference": "1dbd3cb01c5645f3e7deda7aa46ef780d95fcc33", + "url": "https://api.github.com/repos/briannesbitt/Carbon/zipball/63da8cdf89d7a5efe43aabc794365f6e7b7b8983", + "reference": "63da8cdf89d7a5efe43aabc794365f6e7b7b8983", "shasum": "" }, "require": { @@ -1902,9 +1908,12 @@ "symfony/translation": "~2.6 || ~3.0 || ~4.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2", "phpunit/phpunit": "^4.8.35 || ^5.7" }, + "suggest": { + "friendsofphp/php-cs-fixer": "Needed for the `composer phpcs` command. Allow to automatically fix code style.", + "phpstan/phpstan": "Needed for the `composer phpstan` command. Allow to detect potential errors." + }, "type": "library", "extra": { "laravel": { @@ -1936,7 +1945,7 @@ "datetime", "time" ], - "time": "2018-09-20T19:36:25+00:00" + "time": "2018-11-22T18:23:02+00:00" }, { "name": "paragonie/random_compat", @@ -2211,16 +2220,16 @@ }, { "name": "psr/log", - "version": "1.0.2", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", + "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd", "shasum": "" }, "require": { @@ -2254,7 +2263,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2018-11-20T15:27:04+00:00" }, { "name": "psr/simple-cache", @@ -2304,6 +2313,46 @@ ], "time": "2017-10-23T01:57:42+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "2.0.5", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "reference": "5601c8a83fbba7ef674a7369456d12f1e0d0eafa", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "require-dev": { + "phpunit/phpunit": "~3.7.0", + "satooshi/php-coveralls": ">=1.0" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2016-02-11T07:05:27+00:00" + }, { "name": "ramsey/uuid", "version": "3.8.0", @@ -3667,16 +3716,16 @@ "packages-dev": [ { "name": "barryvdh/laravel-debugbar", - "version": "v3.2.0", + "version": "v3.2.1", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-debugbar.git", - "reference": "5b68f3972083a7eeec0d6f161962fcda71a127c0" + "reference": "9d5caf43c5f3a3aea2178942f281054805872e7c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/5b68f3972083a7eeec0d6f161962fcda71a127c0", - "reference": "5b68f3972083a7eeec0d6f161962fcda71a127c0", + "url": "https://api.github.com/repos/barryvdh/laravel-debugbar/zipball/9d5caf43c5f3a3aea2178942f281054805872e7c", + "reference": "9d5caf43c5f3a3aea2178942f281054805872e7c", "shasum": "" }, "require": { @@ -3731,24 +3780,24 @@ "profiler", "webprofiler" ], - "time": "2018-08-22T11:06:19+00:00" + "time": "2018-11-09T08:37:55+00:00" }, { "name": "barryvdh/laravel-ide-helper", - "version": "v2.5.1", + "version": "v2.5.3", "source": { "type": "git", "url": "https://github.com/barryvdh/laravel-ide-helper.git", - "reference": "7db1843473e1562d8e0490b51db847d3a1415140" + "reference": "3d7f1240896a075aa23b13f82dfcbe165dadeef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/7db1843473e1562d8e0490b51db847d3a1415140", - "reference": "7db1843473e1562d8e0490b51db847d3a1415140", + "url": "https://api.github.com/repos/barryvdh/laravel-ide-helper/zipball/3d7f1240896a075aa23b13f82dfcbe165dadeef2", + "reference": "3d7f1240896a075aa23b13f82dfcbe165dadeef2", "shasum": "" }, "require": { - "barryvdh/reflection-docblock": "^2.0.4", + "barryvdh/reflection-docblock": "^2.0.6", "composer/composer": "^1.6", "illuminate/console": "^5.5,<5.8", "illuminate/filesystem": "^5.5,<5.8", @@ -3805,20 +3854,20 @@ "phpstorm", "sublime" ], - "time": "2018-09-06T18:41:09+00:00" + "time": "2018-12-19T12:12:05+00:00" }, { "name": "barryvdh/reflection-docblock", - "version": "v2.0.4", + "version": "v2.0.6", "source": { "type": "git", "url": "https://github.com/barryvdh/ReflectionDocBlock.git", - "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c" + "reference": "6b69015d83d3daf9004a71a89f26e27d27ef6a16" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/3dcbd98b5d9384a5357266efba8fd29884458e5c", - "reference": "3dcbd98b5d9384a5357266efba8fd29884458e5c", + "url": "https://api.github.com/repos/barryvdh/ReflectionDocBlock/zipball/6b69015d83d3daf9004a71a89f26e27d27ef6a16", + "reference": "6b69015d83d3daf9004a71a89f26e27d27ef6a16", "shasum": "" }, "require": { @@ -3854,7 +3903,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2016-06-13T19:28:20+00:00" + "time": "2018-12-13T10:34:14+00:00" }, { "name": "composer/ca-bundle", @@ -3914,16 +3963,16 @@ }, { "name": "composer/composer", - "version": "1.7.3", + "version": "1.8.0", "source": { "type": "git", "url": "https://github.com/composer/composer.git", - "reference": "e965b9aaa8854c3067f1ed2ae45f436572d73eb7" + "reference": "d8aef3af866b28786ce9b8647e52c42496436669" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/composer/zipball/e965b9aaa8854c3067f1ed2ae45f436572d73eb7", - "reference": "e965b9aaa8854c3067f1ed2ae45f436572d73eb7", + "url": "https://api.github.com/repos/composer/composer/zipball/d8aef3af866b28786ce9b8647e52c42496436669", + "reference": "d8aef3af866b28786ce9b8647e52c42496436669", "shasum": "" }, "require": { @@ -3959,7 +4008,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.7-dev" + "dev-master": "1.8-dev" } }, "autoload": { @@ -3990,7 +4039,7 @@ "dependency", "package" ], - "time": "2018-11-01T09:05:06+00:00" + "time": "2018-12-03T09:31:16+00:00" }, { "name": "composer/semver", @@ -4117,16 +4166,16 @@ }, { "name": "composer/xdebug-handler", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + "reference": "dc523135366eb68f22268d069ea7749486458562" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/dc523135366eb68f22268d069ea7749486458562", + "reference": "dc523135366eb68f22268d069ea7749486458562", "shasum": "" }, "require": { @@ -4157,7 +4206,7 @@ "Xdebug", "performance" ], - "time": "2018-08-31T19:07:57+00:00" + "time": "2018-11-29T10:59:02+00:00" }, { "name": "doctrine/instantiator", @@ -6020,16 +6069,16 @@ }, { "name": "squizlabs/php_codesniffer", - "version": "3.3.2", + "version": "3.4.0", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "6ad28354c04b364c3c71a34e4a18b629cc3b231e" + "reference": "379deb987e26c7cd103a7b387aea178baec96e48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/6ad28354c04b364c3c71a34e4a18b629cc3b231e", - "reference": "6ad28354c04b364c3c71a34e4a18b629cc3b231e", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/379deb987e26c7cd103a7b387aea178baec96e48", + "reference": "379deb987e26c7cd103a7b387aea178baec96e48", "shasum": "" }, "require": { @@ -6067,7 +6116,7 @@ "phpcs", "standards" ], - "time": "2018-09-23T23:08:17+00:00" + "time": "2018-12-19T23:57:18+00:00" }, { "name": "symfony/dom-crawler", @@ -6271,12 +6320,16 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0.0", + "php": ">=7.0.5", + "ext-json": "*", "ext-tidy": "*", - "ext-dom": "*" + "ext-dom": "*", + "ext-xml": "*", + "ext-mbstring": "*", + "ext-gd": "*" }, "platform-dev": [], "platform-overrides": { - "php": "7.0" + "php": "7.0.5" } } diff --git a/config/app.php b/config/app.php index b514263d1..e2885d196 100755 --- a/config/app.php +++ b/config/app.php @@ -1,175 +1,84 @@ env('APP_ENV', 'production'), - /** - * Set the default view type for various lists. Can be overridden by user preferences. - * This will be used for public viewers and users that have not set a preference. - */ + // Enter the application in debug mode. + // Shows much more verbose error messages. Has potential to show + // private configuration variables so should remain disabled in public. + 'debug' => env('APP_DEBUG', false), + + // Set the default view type for various lists. Can be overridden by user preferences. + // These will be used for public viewers and users that have not set a preference. 'views' => [ 'books' => env('APP_VIEWS_BOOKS', 'list') ], - /** - * The number of revisions to keep in the database. - * Once this limit is reached older revisions will be deleted. - * If set to false then a limit will not be enforced. - */ + // The number of revisions to keep in the database. + // Once this limit is reached older revisions will be deleted. + // If set to false then a limit will not be enforced. 'revision_limit' => env('REVISION_LIMIT', 50), - /** - * Allow