From 88e148ba0046f96a16b57d978b1f2cc89e6a4f20 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Thu, 10 Aug 2023 15:44:27 +0200 Subject: [PATCH 01/13] Initial Draft --- app/App/HomeController.php | 150 ++++++++++++++++++------- resources/views/layouts/base.blade.php | 4 + routes/web.php | 1 + 3 files changed, 112 insertions(+), 43 deletions(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index 667af80d3..2c57da3c1 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -18,41 +18,41 @@ use Illuminate\Http\Request; class HomeController extends Controller { /** - * Display the homepage. - */ + * Display the homepage. + */ public function index(Request $request, ActivityQueries $activities) { $activity = $activities->latest(10); $draftPages = []; - + if ($this->isSignedIn()) { $draftPages = Page::visible() - ->where('draft', '=', true) - ->where('created_by', '=', user()->id) - ->orderBy('updated_at', 'desc') - ->with('book') - ->take(6) - ->get(); + ->where('draft', '=', true) + ->where('created_by', '=', user()->id) + ->orderBy('updated_at', 'desc') + ->with('book') + ->take(6) + ->get(); } - + $recentFactor = count($draftPages) > 0 ? 0.5 : 1; $recents = $this->isSignedIn() ? - (new RecentlyViewed())->run(12 * $recentFactor, 1) - : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); + (new RecentlyViewed())->run(12 * $recentFactor, 1) + : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); $favourites = (new TopFavourites())->run(6); $recentlyUpdatedPages = Page::visible()->with('book') - ->where('draft', false) - ->orderBy('updated_at', 'desc') - ->take($favourites->count() > 0 ? 5 : 10) - ->select(Page::$listAttributes) - ->get(); - + ->where('draft', false) + ->orderBy('updated_at', 'desc') + ->take($favourites->count() > 0 ? 5 : 10) + ->select(Page::$listAttributes) + ->get(); + $homepageOptions = ['default', 'books', 'bookshelves', 'page']; $homepageOption = setting('app-homepage-type', 'default'); if (!in_array($homepageOption, $homepageOptions)) { $homepageOption = 'default'; } - + $commonData = [ 'activity' => $activity, 'recents' => $recents, @@ -60,7 +60,7 @@ class HomeController extends Controller 'draftPages' => $draftPages, 'favourites' => $favourites, ]; - + // Add required list ordering & sorting for books & shelves views. if ($homepageOption === 'bookshelves' || $homepageOption === 'books') { $key = $homepageOption; @@ -70,27 +70,27 @@ class HomeController extends Controller 'created_at' => trans('common.sort_created_at'), 'updated_at' => trans('common.sort_updated_at'), ]); - + $commonData = array_merge($commonData, [ 'view' => $view, 'listOptions' => $listOptions, ]); } - + if ($homepageOption === 'bookshelves') { $shelves = app(BookshelfRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); $data = array_merge($commonData, ['shelves' => $shelves]); - + return view('home.shelves', $data); } - + if ($homepageOption === 'books') { $books = app(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); $data = array_merge($commonData, ['books' => $books]); - + return view('home.books', $data); } - + if ($homepageOption === 'page') { $homepageSetting = setting('app-homepage', '0:'); $id = intval(explode(':', $homepageSetting)[0]); @@ -98,46 +98,110 @@ class HomeController extends Controller $customHomepage = Page::query()->where('draft', '=', false)->findOrFail($id); $pageContent = new PageContent($customHomepage); $customHomepage->html = $pageContent->render(false); - + return view('home.specific-page', array_merge($commonData, ['customHomepage' => $customHomepage])); } - + return view('home.default', $commonData); } - + /** - * Show the view for /robots.txt. - */ + * Show the view for /robots.txt. + */ public function robots() { $sitePublic = setting('app-public', false); $allowRobots = config('app.allow_robots'); - + if ($allowRobots === null) { $allowRobots = $sitePublic; } - + return response() - ->view('misc.robots', ['allowRobots' => $allowRobots]) - ->header('Content-Type', 'text/plain'); + ->view('misc.robots', ['allowRobots' => $allowRobots]) + ->header('Content-Type', 'text/plain'); } - + /** - * Show the route for 404 responses. - */ + * Show the route for 404 responses. + */ public function notFound() { return response()->view('errors.404', [], 404); } - + /** - * Serve the application favicon. - * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served - * directly by the webserver in the future. - */ + * Serve the application favicon. + * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served + * directly by the webserver in the future. + */ public function favicon(FaviconHandler $favicons) { $exists = $favicons->restoreOriginalIfNotExists(); return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); } + + /** + * Serve the application manifest. + * Ensures a 'manifest.json' + */ + public function manifest() + { + $manifest = [ + "name" => config('app.name' | 'BookStack'), + "short_name" => "bookstack", + "start_url" => "/", + "scope" => "/", + "display" => "standalone", + "background_color" => "#fff", + "description" => config('app.name' | 'BookStack'), + "categories" => [ + "productivity", + "lifestyle" + ], + "launch_handler" => [ + "client_mode" => "focus-existing" + ], + "orientation" => "portrait", + "icons" => [ + [ + "src" => "/icon-64.png", + "sizes" => "64x64", + "type" => "image/png" + ], + [ + "src" => "/icon-32.png", + "sizes" => "32x32", + "type" => "image/png" + ], + [ + "src" => "/icon-128.png", + "sizes" => "128x128", + "type" => "image/png" + ], + [ + "src" => "icon-180.png", + "sizes" => "180x180", + "type" => "image/png" + ], + [ + "src" => "icon.png", + "sizes" => "256x256", + "type" => "image/png" + ], + [ + "src" => "icon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + [ + "src" => "favicon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + ], + ]; + + return response()->json($manifest); + } } diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index e0a6f46d0..4a0422dcd 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -29,6 +29,10 @@ + + + + @yield('head') diff --git a/routes/web.php b/routes/web.php index 74ee74a2c..6e80635e0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -20,6 +20,7 @@ use Illuminate\View\Middleware\ShareErrorsFromSession; Route::get('/status', [SettingControllers\StatusController::class, 'show']); Route::get('/robots.txt', [HomeController::class, 'robots']); Route::get('/favicon.ico', [HomeController::class, 'favicon']); +Route::get('/manifest.json', [HomeController::class, 'manifest']); // Authenticated routes... Route::middleware('auth')->group(function () { From 601491b275a3d7f81007223f52c79af528e337ba Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Thu, 10 Aug 2023 15:51:09 +0200 Subject: [PATCH 02/13] Add Color --- app/App/HomeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index 2c57da3c1..64d2865ec 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -153,7 +153,7 @@ class HomeController extends Controller "start_url" => "/", "scope" => "/", "display" => "standalone", - "background_color" => "#fff", + "background_color" => setting('app-color'), "description" => config('app.name' | 'BookStack'), "categories" => [ "productivity", From 08ea97fd8346edad23440ffe5c65e1f98467e76a Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Thu, 10 Aug 2023 16:43:14 +0200 Subject: [PATCH 03/13] Manifest Tweaks --- app/App/HomeController.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index 64d2865ec..641b84fa8 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -148,13 +148,13 @@ class HomeController extends Controller public function manifest() { $manifest = [ - "name" => config('app.name' | 'BookStack'), + "name" => (config('app.name' | 'BookStack') ??'BookStack' ), "short_name" => "bookstack", - "start_url" => "/", - "scope" => "/", + "start_url" => "./", + "scope" => ".", "display" => "standalone", "background_color" => setting('app-color'), - "description" => config('app.name' | 'BookStack'), + "description" =>( config('app.name' | 'BookStack') ??'BookStack'), "categories" => [ "productivity", "lifestyle" From 2b604b5af9e7a7973f693f7ef7a5d964c0bcadeb Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Thu, 10 Aug 2023 17:02:31 +0200 Subject: [PATCH 04/13] Move Manifest Definition to Separate Config File --- app/App/HomeController.php | 57 ++------------------------------------ app/Config/manifest.php | 55 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 54 deletions(-) create mode 100644 app/Config/manifest.php diff --git a/app/App/HomeController.php b/app/App/HomeController.php index 641b84fa8..d971247df 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -147,61 +147,10 @@ class HomeController extends Controller */ public function manifest() { - $manifest = [ - "name" => (config('app.name' | 'BookStack') ??'BookStack' ), - "short_name" => "bookstack", - "start_url" => "./", - "scope" => ".", - "display" => "standalone", - "background_color" => setting('app-color'), - "description" =>( config('app.name' | 'BookStack') ??'BookStack'), - "categories" => [ - "productivity", - "lifestyle" - ], - "launch_handler" => [ - "client_mode" => "focus-existing" - ], - "orientation" => "portrait", - "icons" => [ - [ - "src" => "/icon-64.png", - "sizes" => "64x64", - "type" => "image/png" - ], - [ - "src" => "/icon-32.png", - "sizes" => "32x32", - "type" => "image/png" - ], - [ - "src" => "/icon-128.png", - "sizes" => "128x128", - "type" => "image/png" - ], - [ - "src" => "icon-180.png", - "sizes" => "180x180", - "type" => "image/png" - ], - [ - "src" => "icon.png", - "sizes" => "256x256", - "type" => "image/png" - ], - [ - "src" => "icon.ico", - "sizes" => "48x48", - "type" => "image/vnd.microsoft.icon" - ], - [ - "src" => "favicon.ico", - "sizes" => "48x48", - "type" => "image/vnd.microsoft.icon" - ], - ], - ]; + $manifest = config('manifest'); + $manifest["background_color"] = setting('app-color'); + return response()->json($manifest); } } diff --git a/app/Config/manifest.php b/app/Config/manifest.php new file mode 100644 index 000000000..640ba70e6 --- /dev/null +++ b/app/Config/manifest.php @@ -0,0 +1,55 @@ + (env('APP_NAME' | 'BookStack') ??'BookStack' ), + "short_name" => "bookstack", + "start_url" => "./", + "scope" => ".", + "display" => "standalone", + "background_color" => "#fff", + "description" =>( env('APP_NAME' | 'BookStack') ??'BookStack'), + "categories" => [ + "productivity", + "lifestyle" + ], + "launch_handler" => [ + "client_mode" => "focus-existing" + ], + "orientation" => "portrait", + "icons" => [ + [ + "src" => "/icon-64.png", + "sizes" => "64x64", + "type" => "image/png" + ], + [ + "src" => "/icon-32.png", + "sizes" => "32x32", + "type" => "image/png" + ], + [ + "src" => "/icon-128.png", + "sizes" => "128x128", + "type" => "image/png" + ], + [ + "src" => "icon-180.png", + "sizes" => "180x180", + "type" => "image/png" + ], + [ + "src" => "icon.png", + "sizes" => "256x256", + "type" => "image/png" + ], + [ + "src" => "icon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + [ + "src" => "favicon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + ], +]; \ No newline at end of file From cb9c3fc9f5a0f0bdca03cdeb8b6446155cdc2cd4 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 10:49:37 +0200 Subject: [PATCH 05/13] Fix Dark theme --- app/App/HomeController.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index d971247df..a2bb151de 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -148,8 +148,12 @@ class HomeController extends Controller public function manifest() { $manifest = config('manifest'); - - $manifest["background_color"] = setting('app-color'); + + if (setting()->getForCurrentUser('dark-mode-enabled')){ + $manifest["background_color"] = setting('app-color-dark'); + }else{ + $manifest["background_color"] = setting('app-color'); + } return response()->json($manifest); } From f910424fa3190d7d8fb15fa79f776ec77b2910a6 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:00:41 +0200 Subject: [PATCH 06/13] Implementation of required changes --- app/App/PwaManifestBuilder.php | 83 ++++++++++++++++++++++++++++++++++ app/Config/manifest.php | 55 ---------------------- routes/web.php | 3 +- 3 files changed, 85 insertions(+), 56 deletions(-) create mode 100644 app/App/PwaManifestBuilder.php delete mode 100644 app/Config/manifest.php diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php new file mode 100644 index 000000000..4c517072c --- /dev/null +++ b/app/App/PwaManifestBuilder.php @@ -0,0 +1,83 @@ + config('app.name'), + "short_name" => config('app.name'), + "start_url" => "./", + "scope" => ".", + "display" => "standalone", + "background_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), + "description" => config('app.name'), + "theme_color" => setting('app-color'), + "launch_handler" => [ + "client_mode" => "focus-existing" + ], + "orientation" => "portrait", + "icons" => [ + [ + "src" => setting('app-icon-64') ?: url('/icon-64.png'), + "sizes" => "64x64", + "type" => "image/png" + ], + [ + "src" => setting('app-icon-32') ?: url('/icon-32.png'), + "sizes" => "32x32", + "type" => "image/png" + ], + [ + "src" => setting('app-icon-128') ?: url('/icon-128.png'), + "sizes" => "128x128", + "type" => "image/png" + ], + [ + "src" => setting('app-icon-180') ?: url('/icon-180.png'), + "sizes" => "180x180", + "type" => "image/png" + ], + [ + "src" => setting('app-icon') ?: url('/icon.png'), + "sizes" => "256x256", + "type" => "image/png" + ], + [ + "src" => "icon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + [ + "src" => "favicon.ico", + "sizes" => "48x48", + "type" => "image/vnd.microsoft.icon" + ], + ], + ]; + } + + /** + * Serve the application manifest. + * Ensures a 'manifest.json' + */ + public function manifest() + { + return response()->json($this->GenerateManifest()); + } +} diff --git a/app/Config/manifest.php b/app/Config/manifest.php deleted file mode 100644 index 640ba70e6..000000000 --- a/app/Config/manifest.php +++ /dev/null @@ -1,55 +0,0 @@ - (env('APP_NAME' | 'BookStack') ??'BookStack' ), - "short_name" => "bookstack", - "start_url" => "./", - "scope" => ".", - "display" => "standalone", - "background_color" => "#fff", - "description" =>( env('APP_NAME' | 'BookStack') ??'BookStack'), - "categories" => [ - "productivity", - "lifestyle" - ], - "launch_handler" => [ - "client_mode" => "focus-existing" - ], - "orientation" => "portrait", - "icons" => [ - [ - "src" => "/icon-64.png", - "sizes" => "64x64", - "type" => "image/png" - ], - [ - "src" => "/icon-32.png", - "sizes" => "32x32", - "type" => "image/png" - ], - [ - "src" => "/icon-128.png", - "sizes" => "128x128", - "type" => "image/png" - ], - [ - "src" => "icon-180.png", - "sizes" => "180x180", - "type" => "image/png" - ], - [ - "src" => "icon.png", - "sizes" => "256x256", - "type" => "image/png" - ], - [ - "src" => "icon.ico", - "sizes" => "48x48", - "type" => "image/vnd.microsoft.icon" - ], - [ - "src" => "favicon.ico", - "sizes" => "48x48", - "type" => "image/vnd.microsoft.icon" - ], - ], -]; \ No newline at end of file diff --git a/routes/web.php b/routes/web.php index 8116cdaf8..6bc563480 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,6 +5,7 @@ use BookStack\Activity\Controllers as ActivityControllers; use BookStack\Api\ApiDocsController; use BookStack\Api\UserApiTokenController; use BookStack\App\HomeController; +use BookStack\App\PwaManifestBuilder; use BookStack\Entities\Controllers as EntityControllers; use BookStack\Http\Middleware\VerifyCsrfToken; use BookStack\Permissions\PermissionsController; @@ -20,7 +21,7 @@ use Illuminate\View\Middleware\ShareErrorsFromSession; Route::get('/status', [SettingControllers\StatusController::class, 'show']); Route::get('/robots.txt', [HomeController::class, 'robots']); Route::get('/favicon.ico', [HomeController::class, 'favicon']); -Route::get('/manifest.json', [HomeController::class, 'manifest']); +Route::get('/manifest.json', [PwaManifestBuilder::class, 'manifest']); // Authenticated routes... Route::middleware('auth')->group(function () { From 9b99664bff7e25da85b3798db408a8294a35b80b Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:15:13 +0200 Subject: [PATCH 07/13] Additional Tweaks and FIxes --- app/App/PwaManifestBuilder.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php index 4c517072c..533c48413 100644 --- a/app/App/PwaManifestBuilder.php +++ b/app/App/PwaManifestBuilder.php @@ -20,13 +20,13 @@ class PwaManifestBuilder extends Controller private function GenerateManifest() { return [ - "name" => config('app.name'), - "short_name" => config('app.name'), + "name" => setting('app-name'), + "short_name" => setting('app-name'), "start_url" => "./", - "scope" => ".", + "scope" => "/", "display" => "standalone", "background_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), - "description" => config('app.name'), + "description" => setting('app-name'), "theme_color" => setting('app-color'), "launch_handler" => [ "client_mode" => "focus-existing" From 2a2f893fcc045cd1bf7c70d0f40edfed389b1dce Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:18:10 +0200 Subject: [PATCH 08/13] Formating Fixes --- app/App/HomeController.php | 105 ++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 61 deletions(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index d0b326c8a..ab62e6d57 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -18,41 +18,41 @@ use Illuminate\Http\Request; class HomeController extends Controller { /** - * Display the homepage. - */ + * Display the homepage. + */ public function index(Request $request, ActivityQueries $activities) { $activity = $activities->latest(10); $draftPages = []; - + if ($this->isSignedIn()) { $draftPages = Page::visible() - ->where('draft', '=', true) - ->where('created_by', '=', user()->id) - ->orderBy('updated_at', 'desc') - ->with('book') - ->take(6) - ->get(); + ->where('draft', '=', true) + ->where('created_by', '=', user()->id) + ->orderBy('updated_at', 'desc') + ->with('book') + ->take(6) + ->get(); } - + $recentFactor = count($draftPages) > 0 ? 0.5 : 1; $recents = $this->isSignedIn() ? - (new RecentlyViewed())->run(12 * $recentFactor, 1) - : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); + (new RecentlyViewed())->run(12 * $recentFactor, 1) + : Book::visible()->orderBy('created_at', 'desc')->take(12 * $recentFactor)->get(); $favourites = (new TopFavourites())->run(6); $recentlyUpdatedPages = Page::visible()->with('book') - ->where('draft', false) - ->orderBy('updated_at', 'desc') - ->take($favourites->count() > 0 ? 5 : 10) - ->select(Page::$listAttributes) - ->get(); - + ->where('draft', false) + ->orderBy('updated_at', 'desc') + ->take($favourites->count() > 0 ? 5 : 10) + ->select(Page::$listAttributes) + ->get(); + $homepageOptions = ['default', 'books', 'bookshelves', 'page']; $homepageOption = setting('app-homepage-type', 'default'); if (!in_array($homepageOption, $homepageOptions)) { $homepageOption = 'default'; } - + $commonData = [ 'activity' => $activity, 'recents' => $recents, @@ -60,7 +60,7 @@ class HomeController extends Controller 'draftPages' => $draftPages, 'favourites' => $favourites, ]; - + // Add required list ordering & sorting for books & shelves views. if ($homepageOption === 'bookshelves' || $homepageOption === 'books') { $key = $homepageOption; @@ -70,27 +70,27 @@ class HomeController extends Controller 'created_at' => trans('common.sort_created_at'), 'updated_at' => trans('common.sort_updated_at'), ]); - + $commonData = array_merge($commonData, [ 'view' => $view, 'listOptions' => $listOptions, ]); } - + if ($homepageOption === 'bookshelves') { $shelves = app()->make(BookshelfRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); $data = array_merge($commonData, ['shelves' => $shelves]); - + return view('home.shelves', $data); } - + if ($homepageOption === 'books') { $books = app()->make(BookRepo::class)->getAllPaginated(18, $commonData['listOptions']->getSort(), $commonData['listOptions']->getOrder()); $data = array_merge($commonData, ['books' => $books]); - + return view('home.books', $data); } - + if ($homepageOption === 'page') { $homepageSetting = setting('app-homepage', '0:'); $id = intval(explode(':', $homepageSetting)[0]); @@ -98,63 +98,46 @@ class HomeController extends Controller $customHomepage = Page::query()->where('draft', '=', false)->findOrFail($id); $pageContent = new PageContent($customHomepage); $customHomepage->html = $pageContent->render(false); - + return view('home.specific-page', array_merge($commonData, ['customHomepage' => $customHomepage])); } - + return view('home.default', $commonData); } - + /** - * Show the view for /robots.txt. - */ + * Show the view for /robots.txt. + */ public function robots() { $sitePublic = setting('app-public', false); $allowRobots = config('app.allow_robots'); - + if ($allowRobots === null) { $allowRobots = $sitePublic; } - + return response() - ->view('misc.robots', ['allowRobots' => $allowRobots]) - ->header('Content-Type', 'text/plain'); + ->view('misc.robots', ['allowRobots' => $allowRobots]) + ->header('Content-Type', 'text/plain'); } - + /** - * Show the route for 404 responses. - */ + * Show the route for 404 responses. + */ public function notFound() { return response()->view('errors.404', [], 404); } - + /** - * Serve the application favicon. - * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served - * directly by the webserver in the future. - */ + * Serve the application favicon. + * Ensures a 'favicon.ico' file exists at the web root location (if writable) to be served + * directly by the webserver in the future. + */ public function favicon(FaviconHandler $favicons) { $exists = $favicons->restoreOriginalIfNotExists(); return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); } - - /** - * Serve the application manifest. - * Ensures a 'manifest.json' - */ - public function manifest() - { - $manifest = config('manifest'); - - if (setting()->getForCurrentUser('dark-mode-enabled')){ - $manifest["background_color"] = setting('app-color-dark'); - }else{ - $manifest["background_color"] = setting('app-color'); - } - - return response()->json($manifest); - } -} +} \ No newline at end of file From 7e09c9a14725954cca5cc987f35b2bfed42d2057 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:19:17 +0200 Subject: [PATCH 09/13] Update HomeController.php --- app/App/HomeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index ab62e6d57..24b7c3ed8 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -140,4 +140,4 @@ class HomeController extends Controller $exists = $favicons->restoreOriginalIfNotExists(); return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); } -} \ No newline at end of file +} From 10e8e1a88dcec88b5393537f509c3d266e224fe7 Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:19:34 +0200 Subject: [PATCH 10/13] New line fix --- app/App/HomeController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/App/HomeController.php b/app/App/HomeController.php index ab62e6d57..24b7c3ed8 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -140,4 +140,4 @@ class HomeController extends Controller $exists = $favicons->restoreOriginalIfNotExists(); return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); } -} \ No newline at end of file +} From 57791c14663ffa19f22e77b66a93b66d7f103dbf Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Fri, 22 Sep 2023 11:31:24 +0200 Subject: [PATCH 11/13] Fix Reloading changes on dark mode switch --- app/App/PwaManifestBuilder.php | 21 +++++++-------------- resources/views/layouts/base.blade.php | 4 ++-- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php index 533c48413..f18e01248 100644 --- a/app/App/PwaManifestBuilder.php +++ b/app/App/PwaManifestBuilder.php @@ -2,23 +2,16 @@ namespace BookStack\App; -use BookStack\Activity\ActivityQueries; -use BookStack\Entities\Models\Book; -use BookStack\Entities\Models\Page; -use BookStack\Entities\Queries\RecentlyViewed; -use BookStack\Entities\Queries\TopFavourites; -use BookStack\Entities\Repos\BookRepo; -use BookStack\Entities\Repos\BookshelfRepo; -use BookStack\Entities\Tools\PageContent; use BookStack\Http\Controller; -use BookStack\Uploads\FaviconHandler; -use BookStack\Util\SimpleListOptions; -use Illuminate\Http\Request; class PwaManifestBuilder extends Controller { private function GenerateManifest() { + dump(setting()->getForCurrentUser('dark-mode-enabled')); + dump(setting('app-color-dark')); + dump(setting('app-color')); + return [ "name" => setting('app-name'), "short_name" => setting('app-name'), @@ -27,7 +20,7 @@ class PwaManifestBuilder extends Controller "display" => "standalone", "background_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), "description" => setting('app-name'), - "theme_color" => setting('app-color'), + "theme_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), "launch_handler" => [ "client_mode" => "focus-existing" ], @@ -59,12 +52,12 @@ class PwaManifestBuilder extends Controller "type" => "image/png" ], [ - "src" => "icon.ico", + "src" => public_path('icon.ico'), "sizes" => "48x48", "type" => "image/vnd.microsoft.icon" ], [ - "src" => "favicon.ico", + "src" => public_path('favicon.ico'), "sizes" => "48x48", "type" => "image/vnd.microsoft.icon" ], diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 69a7e148e..13ad6a4fd 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -10,7 +10,7 @@ - + @@ -31,7 +31,7 @@ - + @yield('head') From 287ed4ff3bfdfc1336e40791cf90337b3a9459fb Mon Sep 17 00:00:00 2001 From: JonatanRek Date: Sun, 24 Sep 2023 20:19:53 +0200 Subject: [PATCH 12/13] Remove Dumps --- app/App/PwaManifestBuilder.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php index f18e01248..377e577eb 100644 --- a/app/App/PwaManifestBuilder.php +++ b/app/App/PwaManifestBuilder.php @@ -8,10 +8,6 @@ class PwaManifestBuilder extends Controller { private function GenerateManifest() { - dump(setting()->getForCurrentUser('dark-mode-enabled')); - dump(setting('app-color-dark')); - dump(setting('app-color')); - return [ "name" => setting('app-name'), "short_name" => setting('app-name'), From 1d91b4d8a66fa5e9b6ae48521d08c55300c5bcfc Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Mon, 2 Oct 2023 15:54:39 +0100 Subject: [PATCH 13/13] PWA Manifest: Tweaks during review of PR #4430 - Updated to go through HomeController with the builder as a helper class. - Extracted some reapeated items into variables in manifest. - Updated background color to match those used by BookStack. - Removed reference of icon.ico since its not intended to be used. - Added tests to cover functionality. Review of #4430 --- app/App/HomeController.php | 8 +++ app/App/PwaManifestBuilder.php | 43 ++++++--------- resources/views/layouts/base.blade.php | 4 +- routes/web.php | 3 +- tests/PwaManifestTest.php | 72 ++++++++++++++++++++++++++ 5 files changed, 98 insertions(+), 32 deletions(-) create mode 100644 tests/PwaManifestTest.php diff --git a/app/App/HomeController.php b/app/App/HomeController.php index 24b7c3ed8..8188ad010 100644 --- a/app/App/HomeController.php +++ b/app/App/HomeController.php @@ -140,4 +140,12 @@ class HomeController extends Controller $exists = $favicons->restoreOriginalIfNotExists(); return response()->file($exists ? $favicons->getPath() : $favicons->getOriginalPath()); } + + /** + * Serve a PWA application manifest. + */ + public function pwaManifest(PwaManifestBuilder $manifestBuilder) + { + return response()->json($manifestBuilder->build()); + } } diff --git a/app/App/PwaManifestBuilder.php b/app/App/PwaManifestBuilder.php index 377e577eb..4902d354d 100644 --- a/app/App/PwaManifestBuilder.php +++ b/app/App/PwaManifestBuilder.php @@ -2,34 +2,35 @@ namespace BookStack\App; -use BookStack\Http\Controller; - -class PwaManifestBuilder extends Controller +class PwaManifestBuilder { - private function GenerateManifest() + public function build(): array { + $darkMode = (bool) setting()->getForCurrentUser('dark-mode-enabled'); + $appName = setting('app-name'); + return [ - "name" => setting('app-name'), - "short_name" => setting('app-name'), + "name" => $appName, + "short_name" => $appName, "start_url" => "./", "scope" => "/", "display" => "standalone", - "background_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), - "description" => setting('app-name'), - "theme_color" => (setting()->getForCurrentUser('dark-mode-enabled') ? setting('app-color-dark') : setting('app-color')), + "background_color" => $darkMode ? '#111111' : '#F2F2F2', + "description" => $appName, + "theme_color" => ($darkMode ? setting('app-color-dark') : setting('app-color')), "launch_handler" => [ "client_mode" => "focus-existing" ], "orientation" => "portrait", "icons" => [ [ - "src" => setting('app-icon-64') ?: url('/icon-64.png'), - "sizes" => "64x64", + "src" => setting('app-icon-32') ?: url('/icon-32.png'), + "sizes" => "32x32", "type" => "image/png" ], [ - "src" => setting('app-icon-32') ?: url('/icon-32.png'), - "sizes" => "32x32", + "src" => setting('app-icon-64') ?: url('/icon-64.png'), + "sizes" => "64x64", "type" => "image/png" ], [ @@ -48,25 +49,11 @@ class PwaManifestBuilder extends Controller "type" => "image/png" ], [ - "src" => public_path('icon.ico'), - "sizes" => "48x48", - "type" => "image/vnd.microsoft.icon" - ], - [ - "src" => public_path('favicon.ico'), + "src" => url('favicon.ico'), "sizes" => "48x48", "type" => "image/vnd.microsoft.icon" ], ], ]; } - - /** - * Serve the application manifest. - * Ensures a 'manifest.json' - */ - public function manifest() - { - return response()->json($this->GenerateManifest()); - } } diff --git a/resources/views/layouts/base.blade.php b/resources/views/layouts/base.blade.php index 13ad6a4fd..8693f021d 100644 --- a/resources/views/layouts/base.blade.php +++ b/resources/views/layouts/base.blade.php @@ -30,8 +30,8 @@ - - + + @yield('head') diff --git a/routes/web.php b/routes/web.php index 6bc563480..9c049ba36 100644 --- a/routes/web.php +++ b/routes/web.php @@ -5,7 +5,6 @@ use BookStack\Activity\Controllers as ActivityControllers; use BookStack\Api\ApiDocsController; use BookStack\Api\UserApiTokenController; use BookStack\App\HomeController; -use BookStack\App\PwaManifestBuilder; use BookStack\Entities\Controllers as EntityControllers; use BookStack\Http\Middleware\VerifyCsrfToken; use BookStack\Permissions\PermissionsController; @@ -21,7 +20,7 @@ use Illuminate\View\Middleware\ShareErrorsFromSession; Route::get('/status', [SettingControllers\StatusController::class, 'show']); Route::get('/robots.txt', [HomeController::class, 'robots']); Route::get('/favicon.ico', [HomeController::class, 'favicon']); -Route::get('/manifest.json', [PwaManifestBuilder::class, 'manifest']); +Route::get('/manifest.json', [HomeController::class, 'pwaManifest']); // Authenticated routes... Route::middleware('auth')->group(function () { diff --git a/tests/PwaManifestTest.php b/tests/PwaManifestTest.php new file mode 100644 index 000000000..b8317321d --- /dev/null +++ b/tests/PwaManifestTest.php @@ -0,0 +1,72 @@ +setSettings(['app-color' => '#00ACED']); + + $resp = $this->get('/manifest.json'); + $resp->assertOk(); + + $resp->assertJson([ + 'name' => setting('app-name'), + 'launch_handler' => [ + 'client_mode' => 'focus-existing' + ], + 'theme_color' => '#00ACED', + ]); + } + + public function test_pwa_meta_tags_in_head() + { + $html = $this->asViewer()->withHtml($this->get('/')); + + // crossorigin attribute is required to send cookies with the manifest, + // so it can react correctly to user preferences (dark/light mode). + $html->assertElementExists('head link[rel="manifest"][href$="manifest.json"][crossorigin="use-credentials"]'); + $html->assertElementExists('head meta[name="mobile-web-app-capable"][content="yes"]'); + } + + public function test_manifest_uses_configured_icons_if_existing() + { + $resp = $this->get('/manifest.json'); + $resp->assertJson([ + 'icons' => [[ + "src" => 'http://localhost/icon-32.png', + "sizes" => "32x32", + "type" => "image/png" + ]] + ]); + + $galleryFile = $this->files->uploadedImage('my-app-icon.png'); + $this->asAdmin()->call('POST', '/settings/customization', [], [], ['app_icon' => $galleryFile], []); + + $customIconUrl = setting()->get('app-icon-32'); + $this->assertStringContainsString('my-app-icon', $customIconUrl); + + $resp = $this->get('/manifest.json'); + $resp->assertJson([ + 'icons' => [[ + "src" => $customIconUrl, + "sizes" => "32x32", + "type" => "image/png" + ]] + ]); + } + + public function test_manifest_changes_to_user_preferences() + { + $lightUser = $this->users->viewer(); + $darkUser = $this->users->editor(); + setting()->putUser($darkUser, 'dark-mode-enabled', 'true'); + + $resp = $this->actingAs($lightUser)->get('/manifest.json'); + $resp->assertJson(['background_color' => '#F2F2F2']); + + $resp = $this->actingAs($darkUser)->get('/manifest.json'); + $resp->assertJson(['background_color' => '#111111']); + } +}