diff --git a/app/Http/Controllers/Admin/SettingsController.php b/app/Http/Controllers/Admin/SettingsController.php index 8829ecf271ec3f55a2789d4bf3bec3ac54f256e0..557a1f8d4590a8bf0bec8a82fd27604fbb49a71f 100644 --- a/app/Http/Controllers/Admin/SettingsController.php +++ b/app/Http/Controllers/Admin/SettingsController.php @@ -4,6 +4,7 @@ use App\Http\Controllers\Controller; use App\Mail\AdminInvitationMail; +use App\Models\DbConfig; use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -22,16 +23,30 @@ class SettingsController extends Controller 'label' => 'Général', 'icon' => 'circle-user', ], - [ - 'route' => 'admin.settings.users', - 'label' => 'Utilisateurs', - 'icon' => 'users', - ], ]; + protected bool $isSuperAdmin; + + public function __construct() + { + $this->isSuperAdmin = Auth::user()->email === config('app.protected_account_email'); + + if ($this->isSuperAdmin) { + $this->headerRoutes[] = [ + 'route' => 'admin.settings.users', + 'label' => 'Utilisateurs', + 'icon' => 'users', + ]; + $this->headerRoutes[] = [ + 'route' => 'admin.settings.website-config', + 'label' => 'Configuration du site', + 'icon' => 'gear', + ]; + } + } + public function userListPage(): View { - $isSuperAdmin = auth()->user()->email === config('app.protected_account_email'); $users = User::all(); foreach ($users as $user) { @@ -41,7 +56,7 @@ public function userListPage(): View return view('admin.settings.users', [ 'headerRoutes' => $this->headerRoutes, 'users' => $users, - 'isSuperAdmin' => $isSuperAdmin, + 'isSuperAdmin' => $this->isSuperAdmin, ]); } @@ -52,9 +67,7 @@ public function createUser(Request $request): RedirectResponse 'email' => 'required|string|email|max:255|unique:users', ]); - $isSuperAdmin = Auth::user()->email === config('app.protected_account_email'); - - if (! $isSuperAdmin) { + if (! $this->isSuperAdmin) { return redirect()->route('admin.settings.users') ->with('error', 'Vous n\'avez pas les droits pour effectuer cette action.'); } @@ -73,9 +86,7 @@ public function createUser(Request $request): RedirectResponse public function deleteUser(int $id): RedirectResponse { - $isSuperAdmin = auth()->user()->email === config('app.protected_account_email'); - - if (! $isSuperAdmin) { + if (! $this->isSuperAdmin) { return redirect()->route('admin.settings.users') ->with('error', 'Vous n\'avez pas les droits pour effectuer cette action.'); } @@ -140,4 +151,39 @@ public function updateUserInfos(Request $request): RedirectResponse return redirect()->route('admin.settings.general')->with('success', 'Informations mises à jour.'); } + + public function websiteConfigPage(): RedirectResponse|View + { + if (! $this->isSuperAdmin) { + return redirect()->route('admin.settings.general') + ->with('error', 'Vous n\'avez pas les droits pour effectuer cette action.'); + } + + return view('admin.settings.website-config', [ + 'headerRoutes' => $this->headerRoutes, + ]); + } + + public function updateWebsiteConfig(Request $request): RedirectResponse + { + $request->validate([ + 'maintenance_mode' => 'sometimes|nullable|string|in:true,false', + 'website_name' => 'sometimes|nullable|string|max:255', + ]); + + if (! $this->isSuperAdmin) { + return redirect()->route('admin.settings.general') + ->with('error', 'Vous n\'avez pas les droits pour effectuer cette action.'); + } + + if ($request->has('maintenance_mode')) { + DbConfig::set('maintenance_mode', $request->maintenance_mode); + } + + if ($request->has('website_name')) { + DbConfig::set('website_name', $request->website_name); + } + + return redirect()->route('admin.settings.website-config')->with('success', 'Configuration mise à jour.'); + } } diff --git a/app/Http/Middleware/CheckPrivateModeMiddleware.php b/app/Http/Middleware/CheckPrivateModeMiddleware.php index 7cf814038d6d7cc8d3516ce8e2c0f69652ecd3b1..ca09e19f96a6e7fee067ba578508706656cd8b68 100644 --- a/app/Http/Middleware/CheckPrivateModeMiddleware.php +++ b/app/Http/Middleware/CheckPrivateModeMiddleware.php @@ -2,6 +2,7 @@ namespace App\Http\Middleware; +use App\Models\DbConfig; use Closure; use Illuminate\Http\Request; @@ -9,7 +10,7 @@ class CheckPrivateModeMiddleware { public function handle(Request $request, Closure $next) { - $privateModeEnabled = config('app.private_mode'); + $privateModeEnabled = DbConfig::get('maintenance_mode', 'true'); $privateModeSecret = config('app.private_mode_secret'); $userSecretInput = $request->input('secret'); $secretIsUsable = ! empty($privateModeSecret) && $privateModeSecret === $userSecretInput; diff --git a/app/Models/DbConfig.php b/app/Models/DbConfig.php new file mode 100644 index 0000000000000000000000000000000000000000..cddde72e647769d79782790b8869f4363605175e --- /dev/null +++ b/app/Models/DbConfig.php @@ -0,0 +1,97 @@ +<?php + +namespace App\Models; + +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Facades\Cache; + +class DbConfig extends Model +{ + use HasFactory; + + public $timestamps = false; + + protected $fillable = [ + 'key', + 'value', + ]; + + /** + * Get a configuration value by its key. + * Value types casts: + * - 'true' => true + * - 'false' => false + * - integers => int + * - floats => float + * - strings => string + * + * @param string $key The key of the configuration value. Example: 'app.name' + * @param float|bool|int|string|null $default The default value to return if the configuration does not exist. + * @return float|bool|int|string|null The value of the configuration. + */ + public static function get(string $key, float|bool|int|string|null $default = null): float|bool|int|string|null + { + $config = Cache::rememberForever('config_'.$key, function () use ($key) { + return self::where('key', $key)->first(); + }); + + if (empty($config) || ! $config->exists || $config->value === null) { + return $default; + } + + // Cast to boolean if the value is a boolean + if ($config->value === 'true') { + return true; + } elseif ($config->value === 'false') { + return false; + } + + // Cast to integer if the value is an integer + if (is_numeric($config->value)) { + if (is_float($config->value)) { + return (float) $config->value; + } else { + return (int) $config->value; + } + } + + return $config->value; + } + + /** + * Set a configuration value by its key. + * + * @param string $key The key of the configuration value. Example: 'app.name' + * @param string|int|float|bool $value The value of the configuration. + */ + public static function set(string $key, float|bool|int|string|null $value): void + { + if (Cache::has('config_'.$key)) { + Cache::forget('config_'.$key); + } + + $config = self::where('key', $key)->first(); + + if ($value === null) { + $config?->delete(); + + return; + } + + if ($config === null) { + self::create([ + 'key' => $key, + 'value' => $value, + ]); + } else { + $config->update([ + 'value' => $value, + ]); + } + + Cache::rememberForever('config_'.$key, function () use ($key) { + return self::where('key', $key)->first(); + }); + } +} diff --git a/config/app.php b/config/app.php index 9ea28a014eb03f08d0e32c6887094ee8780bf652..5044bc0983f9c6575abfb3deba3697f93ed9988d 100644 --- a/config/app.php +++ b/config/app.php @@ -127,7 +127,7 @@ 'protected_account_email' => env('PROTECTED_ACCOUNT_EMAIL', ''), - 'private_mode' => env('APP_PRIVATE_MODE', false), + // 'private_mode' => env('APP_PRIVATE_MODE', false), 'imagick' => [ 'max_width' => env('IMAGICK_MAX_WIDTH', 16000), // 16k diff --git a/database/factories/DbConfigFactory.php b/database/factories/DbConfigFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..d1d7eae53fa035167c8d93a7e65764f08269fb1b --- /dev/null +++ b/database/factories/DbConfigFactory.php @@ -0,0 +1,19 @@ +<?php + +namespace Database\Factories; + +use App\Models\DbConfig; +use Illuminate\Database\Eloquent\Factories\Factory; + +class DbConfigFactory extends Factory +{ + protected $model = DbConfig::class; + + public function definition(): array + { + return [ + 'key' => $this->faker->word(), + 'value' => $this->faker->word(), + ]; + } +} diff --git a/database/migrations/2025_02_23_145044_create_db_configs_table.php b/database/migrations/2025_02_23_145044_create_db_configs_table.php new file mode 100644 index 0000000000000000000000000000000000000000..09506350a1f0e6c5e8025e55bdc84ad74a00902f --- /dev/null +++ b/database/migrations/2025_02_23_145044_create_db_configs_table.php @@ -0,0 +1,22 @@ +<?php + +use Illuminate\Database\Migrations\Migration; +use Illuminate\Database\Schema\Blueprint; +use Illuminate\Support\Facades\Schema; + +return new class extends Migration +{ + public function up(): void + { + Schema::create('db_configs', function (Blueprint $table) { + $table->id(); + $table->string('key')->unique(); + $table->string('value'); + }); + } + + public function down(): void + { + Schema::dropIfExists('db_configs'); + } +}; diff --git a/resources/views/admin/home.blade.php b/resources/views/admin/home.blade.php index 65c1ae2470c0d1fe13d53c426d84be4666f88b76..7ab23f545144b56ba4a8f99f5f316d60e7d80428 100644 --- a/resources/views/admin/home.blade.php +++ b/resources/views/admin/home.blade.php @@ -2,6 +2,14 @@ @section('content') <div class="container" id="admin-home"> + @if(\App\Models\DbConfig::get('maintenance_mode', 'true')) + <x-bs.alert type="warning" :dismissible="false" icon="info" title="Information" style="max-width: none;"> + Le site est masqué au public car le mode maintenance est activé.<br> + <a href="{{ route('admin.settings.update-website-config') }}" class="alert-link"> + Rendez-vous sur la page de configuration du site pour le désactiver. + </a> + </x-bs.alert> + @endif <h3 class="mb-4">Statistiques des Visites uniques</h3> <div class="grid mb-4"> diff --git a/resources/views/admin/settings/general.blade.php b/resources/views/admin/settings/general.blade.php index ef891f725d4090d82f6aad10f774fe5fc8668061..6ea34cd993b44b73a28a9be5dd5f29bb6afbe364 100644 --- a/resources/views/admin/settings/general.blade.php +++ b/resources/views/admin/settings/general.blade.php @@ -3,10 +3,32 @@ @section('content') <div class="grid"> <div class="g-col-12 g-col-md-6"> + @if (!empty($errors) && $errors->any()) + <x-bs.alert type="danger" class="mt-3"> + <ul> + @foreach ($errors->all() as $error) + <li>{{ $error }}</li> + @endforeach + </ul> + </x-bs.alert> + @endif + + @if (session('success')) + <x-bs.alert type="success" class="mt-3"> + {{ session('success') }} + </x-bs.alert> + @endif + + @if (session('error')) + <x-bs.alert type="danger" class="mt-3"> + {{ session('error') }} + </x-bs.alert> + @endif + <h5>Information</h5> <p>Ne remplissez que les champs que vous souhaitez mettre à jour.</p> <form action="{{ route('admin.settings.update-user-infos') }}" - method="post"> + method="post"> @csrf @method('PUT') @@ -48,22 +70,6 @@ class="mb-3" <x-bs.button type="submit">Enregistrer</x-bs.button> </form> - - @if (!empty($errors) && $errors->any()) - <x-bs.alert type="danger" class="mt-3"> - <ul> - @foreach ($errors->all() as $error) - <li>{{ $error }}</li> - @endforeach - </ul> - </x-bs.alert> - @endif - - @if (session('success')) - <x-bs.alert type="success" class="mt-3"> - {{ session('success') }} - </x-bs.alert> - @endif </div> </div> @endsection diff --git a/resources/views/admin/settings/users.blade.php b/resources/views/admin/settings/users.blade.php index 57713da07d490c06ac3a5e2197c6641d2ebdc6de..a3bc900930e1c70365180da2a6845f843087cc05 100644 --- a/resources/views/admin/settings/users.blade.php +++ b/resources/views/admin/settings/users.blade.php @@ -7,7 +7,8 @@ par email.</p> <p><span class="fw-bold text-danger">Attention !</span> Pour des raisons de simplicité, ces personnes auront les - mêmes droits que vous (sauf toutes les actions liées à cette page, y compris son accès). + mêmes droits que vous (sauf toutes les actions liées à cette page et à la configuration du site, y compris + leur accès). <strong>Soyez bien certains d'avoir confiance en elles !</strong></p> <x-bs.button data-bs-toggle="modal" data-bs-target="#addUserModal" size="sm" variant="primary"> diff --git a/resources/views/admin/settings/website-config.blade.php b/resources/views/admin/settings/website-config.blade.php new file mode 100644 index 0000000000000000000000000000000000000000..4e15c67a69f6f055c1c0a5c033fe304a6113a167 --- /dev/null +++ b/resources/views/admin/settings/website-config.blade.php @@ -0,0 +1,48 @@ +@extends('layouts.admin', ['title' => 'Configuration', 'headerRoutes' => $headerRoutes]) + +@section('content') + <div class="grid"> + <div class="g-col-12 g-col-md-6"> + @if (!empty($errors) && $errors->any()) + <x-bs.alert type="danger" class="mt-3"> + <ul> + @foreach ($errors->all() as $error) + <li>{{ $error }}</li> + @endforeach + </ul> + </x-bs.alert> + @endif + + @if (session('success')) + <x-bs.alert type="success" class="mt-3"> + {{ session('success') }} + </x-bs.alert> + @endif + + <h5>Configuration</h5> + <p>Modifiez les paramètres du site internet.</p> + <form action="{{ route('admin.settings.update-website-config') }}" + method="post"> + @csrf + @method('PUT') + + <x-bs.select label="Statut du mode maintenance" + name="maintenance_mode" + class="mb-3" + :options="['false' => 'Désactivé', 'true' => 'Activé']" + :selected="old('maintenance_mode', \App\Models\DbConfig::get('maintenance_mode', 'true'))" + data-form-type="other" + /> + + <x-bs.input label="Nom du site internet" + name="website_name" + class="mb-3" + :value="old('website_name', \App\Models\DbConfig::get('website_name', 'Rann Graphic Design'))" + data-form-type="other" + /> + + <x-bs.button type="submit">Enregistrer</x-bs.button> + </form> + </div> + </div> +@endsection diff --git a/resources/views/components/admin/sidebar.blade.php b/resources/views/components/admin/sidebar.blade.php index 8358c73a3c50adced06c560f5e28f5d12c40e1dc..383a4b3d856cbeb330842c8fc4114a68f805359e 100644 --- a/resources/views/components/admin/sidebar.blade.php +++ b/resources/views/components/admin/sidebar.blade.php @@ -8,7 +8,7 @@ <a class="website-name" href="{{ route('admin.home') }}"> <img class="logo" src="{{ asset('favicon.svg') }}" alt="Website Logo"/> <span> - {{ config('app.name') }} + {{ \App\Models\DbConfig::get('website_name', "Rann Graphic Design") }} </span> </a> </div> diff --git a/resources/views/layouts/public.blade.php b/resources/views/layouts/public.blade.php index c465a550b7de938c2efd1668d78a98851c309d9a..51d9b8ef932156641ee214e8fb59941ef0e3a5c5 100644 --- a/resources/views/layouts/public.blade.php +++ b/resources/views/layouts/public.blade.php @@ -1,15 +1,19 @@ +@php + $websiteName = \App\Models\DbConfig::get('website_name', "Rann Graphic Design") +@endphp + <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> - <title>{{ !empty($title) ? $title . ' - Rann Graphic Design' : 'Rann Graphic Design' }}</title> + <title>{{ !empty($title) ? $title . ' - ' . $websiteName : $websiteName }}</title> <link rel="icon" type="image/svg+xml" href="{{ asset('favicon.svg') }}"/> <meta property="og:locale" content="{{ config('app.locale') }}"> <meta property="og:locale:alternate" content="{{ config('app.fallback_locale') }}"> - <meta property="og:title" content="{{ !empty($title) ? $title : 'Rann Graphic Design' }}"> + <meta property="og:title" content="{{ !empty($title) ? $title : $websiteName }}"> <meta property="og:type" content="website"> <meta property="og:url" content="{{ url()->current() }}"> @if(!empty($description)) diff --git a/routes/web.php b/routes/web.php index d006a190a22dd7a0926f7202c57c29cd08fbcdd9..3049d6c2b25ac7f4dede3368ce149133bca9d192 100644 --- a/routes/web.php +++ b/routes/web.php @@ -113,6 +113,8 @@ Route::match(['GET', 'DELETE'], '/users/{id}/delete', [SettingsController::class, 'deleteUser'])->name('delete-user'); Route::get('/general', [SettingsController::class, 'generalPage'])->name('general'); Route::put('/general', [SettingsController::class, 'updateUserInfos'])->name('update-user-infos'); + Route::get('/website-configuration', [SettingsController::class, 'websiteConfigPage'])->name('website-config'); + Route::put('/website-configuration', [SettingsController::class, 'updateWebsiteConfig'])->name('update-website-config'); }); Route::prefix('prestations')->name('prestations.')->group(function () { diff --git a/tests/Feature/ExampleTest.php b/tests/Feature/ExampleTest.php deleted file mode 100644 index 22ef0c599c0a71232c7b2b1ff5c5921e0392c50f..0000000000000000000000000000000000000000 --- a/tests/Feature/ExampleTest.php +++ /dev/null @@ -1,21 +0,0 @@ -<?php - -namespace Tests\Feature; - -use Illuminate\Foundation\Testing\RefreshDatabase; -use Tests\TestCase; - -class ExampleTest extends TestCase -{ - use RefreshDatabase; - - /** - * A basic test example. - */ - public function test_the_application_returns_a_successful_response(): void - { - $response = $this->get('/'); - - $response->assertStatus(200); - } -}