Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
74.19% covered (warning)
74.19%
23 / 31
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
AppServiceProvider
74.19% covered (warning)
74.19%
23 / 31
50.00% covered (danger)
50.00%
1 / 2
10.39
0.00% covered (danger)
0.00%
0 / 1
 register
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 boot
68.00% covered (warning)
68.00%
17 / 25
0.00% covered (danger)
0.00%
0 / 1
10.10
1<?php
2
3namespace App\Providers;
4
5use App\Services\Builder\MenuService;
6use App\Services\Builder\WidgetService;
7use App\Services\Feature\FeatureService;
8use App\Services\Permission\PermissionService;
9use App\Services\Setting\SettingService;
10use App\Services\Theme\ThemeService;
11use Illuminate\Pagination\Paginator;
12use Illuminate\Support\Facades\Blade;
13use Illuminate\Support\Facades\Gate;
14use Illuminate\Support\Facades\View;
15use Illuminate\Support\ServiceProvider;
16use Illuminate\Validation\Rules\Password;
17
18class AppServiceProvider extends ServiceProvider
19{
20    public function register()
21    {
22        $this->app->singleton(SettingService::class);
23        $this->app->singleton(FeatureService::class);
24        $this->app->singleton(PermissionService::class);
25        $this->app->singleton(MenuService::class);
26        $this->app->singleton(WidgetService::class);
27        $this->app->singleton(ThemeService::class);
28    }
29
30    public function boot()
31    {
32        // RBAC bypass: off, or the owner, sees everything. Otherwise fall
33        // through to spatie's normal permission check (Gate::before returns
34        // null to defer).
35        Gate::before(function ($user, string $ability) {
36            $svc = app(PermissionService::class);
37            if (! $svc->enforced()) {
38                return true;
39            }
40            if ((int) $user->id === 1 || $user->hasRole('Owner')) {
41                return true;
42            }
43
44            return null;
45        });
46
47        // FIX: Laravel's default pagination view (`pagination::tailwind`) renders
48        // Tailwind utility classes, but this whole admin panel is built on
49        // Bootstrap 4 with no Tailwind loaded — so every `->links()` call
50        // rendered as unstyled/broken markup. Bootstrap 4 view matches the
51        // theme's own `.pagination`/`.page-item`/`.page-link` classes.
52        Paginator::useBootstrap();
53
54        // @feature('coupons') ... @else ... @endfeature
55        Blade::if('feature', fn (string $key) => feature($key));
56
57        // @allows('manage_products') — respects the rbac flag + owner bypass
58        Blade::if('allows', fn (string $ability) => permissions()->allows($ability));
59
60        // One password policy everywhere (staff, registration, reset):
61        // at least 8 characters with letters and numbers.
62        Password::defaults(fn () => Password::min(8)->letters()->numbers());
63
64        // Phase 11 Slice 4 — "Delete backups older than N days" from
65        // Settings → Backups drives spatie's cleanup. Set it here (not in
66        // config/backup.php) so it still applies when config is cached and
67        // so a DB read isn't done during config loading. Longer retention
68        // tiers are zeroed: the operator asked for one simple cutoff.
69        try {
70            $keepDays = (int) settings('backup.keep_days', 30);
71            if ($keepDays > 0) {
72                config([
73                    'backup.cleanup.default_strategy.keep_all_backups_for_days' => $keepDays,
74                    'backup.cleanup.default_strategy.keep_daily_backups_for_days' => 0,
75                    'backup.cleanup.default_strategy.keep_weekly_backups_for_weeks' => 0,
76                    'backup.cleanup.default_strategy.keep_monthly_backups_for_months' => 0,
77                    'backup.cleanup.default_strategy.keep_yearly_backups_for_years' => 0,
78                ]);
79            }
80        } catch (\Throwable $e) {
81            // app_settings table not migrated yet (fresh install / CI bootstrap)
82        }
83
84        // Theme files are namespaced instead of globally prepending their path.
85        // Shared page/page-builder views therefore cannot be replaced by a
86        // theme. Storefront templates opt in only at visual slots such as the
87        // header, footer, hero and product card via storefront_theme_view().
88        try {
89            if ($path = app(ThemeService::class)->activePath()) {
90                View::addNamespace('theme', $path);
91            }
92        } catch (\Throwable $e) {
93            // themes table not migrated yet (fresh install / CI bootstrap)
94        }
95    }
96}