diff --git a/app/Http/Controllers/SettingController.php b/app/Http/Controllers/SettingController.php index f5e48ca4c..1e13d7cb7 100644 --- a/app/Http/Controllers/SettingController.php +++ b/app/Http/Controllers/SettingController.php @@ -4,20 +4,14 @@ namespace BookStack\Http\Controllers; use BookStack\Actions\ActivityType; use BookStack\Auth\User; +use BookStack\Settings\AppSettingsStore; use BookStack\Uploads\ImageRepo; use Illuminate\Http\Request; class SettingController extends Controller { - protected ImageRepo $imageRepo; - protected array $settingCategories = ['features', 'customization', 'registration']; - public function __construct(ImageRepo $imageRepo) - { - $this->imageRepo = $imageRepo; - } - /** * Handle requests to the settings index path. */ @@ -48,37 +42,17 @@ class SettingController extends Controller /** * Update the specified settings in storage. */ - public function update(Request $request, string $category) + public function update(Request $request, AppSettingsStore $store, string $category) { $this->ensureCategoryExists($category); $this->preventAccessInDemoMode(); $this->checkPermission('settings-manage'); $this->validate($request, [ - 'app_logo' => array_merge(['nullable'], $this->getImageValidationRules()), + 'app_logo' => ['nullable', ...$this->getImageValidationRules()], + 'app_icon' => ['nullable', ...$this->getImageValidationRules()], ]); - // Cycles through posted settings and update them - foreach ($request->all() as $name => $value) { - $key = str_replace('setting-', '', trim($name)); - if (strpos($name, 'setting-') !== 0) { - continue; - } - setting()->put($key, $value); - } - - // Update logo image if set - if ($category === 'customization' && $request->hasFile('app_logo')) { - $logoFile = $request->file('app_logo'); - $this->imageRepo->destroyByType('system'); - $image = $this->imageRepo->saveNew($logoFile, 'system', 0, null, 86); - setting()->put('app-logo', $image->url); - } - - // Clear logo image if requested - if ($category === 'customization' && $request->get('app_logo_reset', null)) { - $this->imageRepo->destroyByType('system'); - setting()->remove('app-logo'); - } + $store->storeFromUpdateRequest($request, $category); $this->logActivity(ActivityType::SETTINGS_UPDATE, $category); $this->showSuccessNotification(trans('settings.settings_save_success')); diff --git a/app/Settings/AppSettingsStore.php b/app/Settings/AppSettingsStore.php new file mode 100644 index 000000000..f2b6cdc52 --- /dev/null +++ b/app/Settings/AppSettingsStore.php @@ -0,0 +1,90 @@ +imageRepo = $imageRepo; + } + + public function storeFromUpdateRequest(Request $request, string $category) + { + $this->storeSimpleSettings($request); + if ($category === 'customization') { + $this->updateAppLogo($request); + $this->updateAppIcon($request); + } + } + + protected function updateAppIcon(Request $request): void + { + $sizes = [128, 64, 32]; + + // Update icon image if set + if ($request->hasFile('app_icon')) { + $iconFile = $request->file('app_icon'); + $this->destroyExistingSettingImage('app-icon'); + $image = $this->imageRepo->saveNew($iconFile, 'system', 0, 256, 256); + setting()->put('app-icon', $image->url); + + foreach ($sizes as $size) { + $icon = $this->imageRepo->saveNew($iconFile, 'system', 0, $size, $size); + setting()->put('app-icon-' . $size, $icon->url); + } + } + + // Clear icon image if requested + if ($request->get('app_icon_reset')) { + $this->destroyExistingSettingImage('app-icon'); + setting()->remove('app-icon'); + foreach ($sizes as $size) { + $this->destroyExistingSettingImage('app-icon-' . $size); + setting()->remove('app-icon-' . $size); + } + } + } + + protected function updateAppLogo(Request $request): void + { + // Update logo image if set + if ($request->hasFile('app_logo')) { + $logoFile = $request->file('app_logo'); + $this->destroyExistingSettingImage('app-logo'); + $image = $this->imageRepo->saveNew($logoFile, 'system', 0, null, 86); + setting()->put('app-logo', $image->url); + } + + // Clear logo image if requested + if ($request->get('app_logo_reset')) { + $this->destroyExistingSettingImage('app-logo'); + setting()->remove('app-logo'); + } + } + + protected function storeSimpleSettings(Request $request): void + { + foreach ($request->all() as $name => $value) { + if (strpos($name, 'setting-') !== 0) { + continue; + } + + $key = str_replace('setting-', '', trim($name)); + setting()->put($key, $value); + } + } + + protected function destroyExistingSettingImage(string $settingKey) + { + $existingVal = setting()->get($settingKey); + if ($existingVal) { + $this->imageRepo->destroyByUrlAndType($existingVal, 'system'); + } + } +} diff --git a/app/Settings/SettingService.php b/app/Settings/SettingService.php index 9f0a41ea2..d1bac164d 100644 --- a/app/Settings/SettingService.php +++ b/app/Settings/SettingService.php @@ -12,15 +12,11 @@ use Illuminate\Contracts\Cache\Repository as Cache; */ class SettingService { - protected $setting; - protected $cache; - protected $localCache = []; + protected Setting $setting; + protected Cache $cache; + protected array $localCache = []; + protected string $cachePrefix = 'setting-'; - protected $cachePrefix = 'setting-'; - - /** - * SettingService constructor. - */ public function __construct(Setting $setting, Cache $cache) { $this->setting = $setting; diff --git a/app/Uploads/ImageRepo.php b/app/Uploads/ImageRepo.php index 8770402ad..910248203 100644 --- a/app/Uploads/ImageRepo.php +++ b/app/Uploads/ImageRepo.php @@ -180,13 +180,17 @@ class ImageRepo } /** - * Destroy all images of a certain type. + * Destroy images that have a specific URL and type combination. * * @throws Exception */ - public function destroyByType(string $imageType): void + public function destroyByUrlAndType(string $url, string $imageType): void { - $images = Image::query()->where('type', '=', $imageType)->get(); + $images = Image::query() + ->where('url', '=', $url) + ->where('type', '=', $imageType) + ->get(); + foreach ($images as $image) { $this->destroyImage($image); } diff --git a/public/icon-128.png b/public/icon-128.png new file mode 100644 index 000000000..46cc2811b Binary files /dev/null and b/public/icon-128.png differ diff --git a/public/icon-32.png b/public/icon-32.png new file mode 100644 index 000000000..7307ed8f1 Binary files /dev/null and b/public/icon-32.png differ diff --git a/public/icon-64.png b/public/icon-64.png new file mode 100644 index 000000000..854d80faa Binary files /dev/null and b/public/icon-64.png differ diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 000000000..b9f0125a8 Binary files /dev/null and b/public/icon.png differ diff --git a/resources/lang/en/settings.php b/resources/lang/en/settings.php index f4204dd68..318dc0a52 100755 --- a/resources/lang/en/settings.php +++ b/resources/lang/en/settings.php @@ -33,7 +33,7 @@ return [ 'app_custom_html_desc' => 'Any content added here will be inserted into the bottom of the section of every page. This is handy for overriding styles or adding analytics code.', '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' => 'Application Logo', - 'app_logo_desc' => 'This image should be 43px in height.
Large images will be scaled down.', + 'app_logo_desc' => 'This is used in the application header bar, among other areas. This image should be 86px in height. Large images will be scaled down.', 'app_primary_color' => 'Application Primary Color', 'app_primary_color_desc' => 'Sets the primary color for the application including the banner, buttons, and links.', 'app_homepage' => 'Application Homepage', diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 76d220952..b09a8dfe9 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -20,6 +20,12 @@ + + + + + + @yield('head') diff --git a/resources/views/settings/customization.blade.php b/resources/views/settings/customization.blade.php index 3748267df..847704007 100644 --- a/resources/views/settings/customization.blade.php +++ b/resources/views/settings/customization.blade.php @@ -53,6 +53,25 @@ +
+
+ +

+ This icon is used for browser tabs and shortcut icons. + This should be a 256px square PNG image. +

+
+
+ @include('form.image-picker', [ + 'removeValue' => 'none', + 'defaultImage' => url('/icon.png'), + 'currentImage' => setting('app-icon'), + 'name' => 'app_icon', + 'imageClass' => 'logo-image', + ]) +
+
+