From 55b6a7842ed8eda41637d3a26f0f7f23c0866478 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Wed, 25 Jan 2023 11:03:19 +0000 Subject: [PATCH] Added ability to control app icon (favicon) via settings --- app/Http/Controllers/SettingController.php | 36 +------ app/Settings/AppSettingsStore.php | 90 ++++++++++++++++++ app/Settings/SettingService.php | 12 +-- app/Uploads/ImageRepo.php | 10 +- public/icon-128.png | Bin 0 -> 3538 bytes public/icon-32.png | Bin 0 -> 1338 bytes public/icon-64.png | Bin 0 -> 1951 bytes public/icon.png | Bin 0 -> 6900 bytes resources/lang/en/settings.php | 2 +- resources/views/layouts/base.blade.php | 6 ++ .../views/settings/customization.blade.php | 19 ++++ 11 files changed, 132 insertions(+), 43 deletions(-) create mode 100644 app/Settings/AppSettingsStore.php create mode 100644 public/icon-128.png create mode 100644 public/icon-32.png create mode 100644 public/icon-64.png create mode 100644 public/icon.png 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 0000000000000000000000000000000000000000..46cc2811b5e7f6162e87b8e4e0cf3785f57a922c GIT binary patch literal 3538 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 M07*qoM6N<$f;;1MtpET3 literal 0 HcmV?d00001 diff --git a/public/icon-32.png b/public/icon-32.png new file mode 100644 index 0000000000000000000000000000000000000000..7307ed8f1e88f1e5ade85925d8aedee40d771f64 GIT binary patch literal 1338 zcmV-A1;zS_P)XIh5Hpmef0y+Cr4mH<;B#mDD+z)v)g|gm$wcm!b;D5H^hqU2< zx8jJk;(@s1iMHc{x#Wts5yXJ|z;E=iCjJoEAyyuF%;gP!Gi@oBKyyJ|% z}>6yamoWkRl#N?92>Yc;n zm&WCu#O9dC=by#rnaJp%#^{>K>7mEzo672=$m*oY>!-=(rONE6%I2xd=cdfa5S}v(4+R(CoC%?5@%6wa@LZ((b9$?zYhGxzOmb)9<>`>9o`7vefXq((1L; z>a*4IywmKq)$6^~?YGwLxYzE!*6z93@4wgYy4mo-*zmjC@xs~hyxj7`+VaNQ?7iLd z#M|@0-R;NQ?Y`dh!QSr3-0#BQ@X6iq!{G7C-to)d^334##^UqGGs*`_uA|D;Oh9@?DOI4`P=RJ;_Um}?)u*D z^ycmM;P3V4?)TyF_vr8V;_>+D@cHBN`Reid|N8&`{Qvv^|Nn}pJ+%M;00DGTPE!Ct=GbNc0004E zOGiWihy@);00009a7bBm001Cy001Cy0qf8I3jhEC%t=H+R5;6(Q$1+HKorhLuqYBr z5$PC;l&(_Tg;F|)Zmz+>-9buMhk`fg zTA^=AF5j1T-}~Nsn)d&QR|ny_dwezIf5N<%2TDPY?Wgdd1Qhz?Tcn-&9{i)OBjd6f zfD141c9SgHS?G7nJpqgst0!QMwkiCzKtEb+5;jp~ol}UYPWblaDvHb>6gLNBpcwJP z5x%2mf4^Q0{g~io$HcxcA6W&F#+RkNaIz6O28gg~L@nbtZU^24px-aFIm`~L?!bk? z*n+UMA8VYvEe?EZ3(mHsS@|B-MjD2qsB-vxwube3S(phTL4cB}M%gDb-%dC+!%MQJn#?AF@z>ObBYR(IVSw*3gG;H)pDiXH6^2U}unO#s{~~X8iEp^8fGp7Zf4e`k7Q;P5=M^07*qoM6N<$f&%O@y#N3J literal 0 HcmV?d00001 diff --git a/public/icon-64.png b/public/icon-64.png new file mode 100644 index 0000000000000000000000000000000000000000..854d80faacb4ddcbbef17a357a5e7c3ca6ed69b9 GIT binary patch literal 1951 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 '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', + ]) +
+
+