diff --git a/README.md b/README.md index 44f526a..8653cf4 100644 --- a/README.md +++ b/README.md @@ -8,3 +8,6 @@   - continue migrations : `php artisan migrate` + +TODO: +- sur les méthodes GET le token est visible dans les logs server.. diff --git a/app/Console/Commands/CreateAdmin.php b/app/Console/Commands/CreateAdmin.php index 59acaf6..bc1d588 100644 --- a/app/Console/Commands/CreateAdmin.php +++ b/app/Console/Commands/CreateAdmin.php @@ -28,18 +28,18 @@ class CreateAdmin extends Command protected $description = 'Create a backend admin user'; /** - * The firstname of the new user. + * The first name of the new user. * * @var string|null */ - protected $firstname; + protected $first_name; /** - * The lastname of the new user. + * The last name of the new user. * * @var string|null */ - protected $lastname; + protected $last_name; /** * The email of the new user. @@ -48,13 +48,6 @@ class CreateAdmin extends Command */ protected $email; - /** - * The password of the new user. - * - * @var string|null - */ - protected $password; - /** * The role of the new user. * @@ -69,27 +62,23 @@ class CreateAdmin extends Command */ public function handle(): int { - $this->firstname = null; + $this->first_name = null; $this->addFirstname(); - $this->lastname = null; + $this->last_name = null; $this->addLastname(); $this->email = null; $this->addEmail(); - $this->password = null; - $this->addPassword(); - $this->role = null; $this->addRole(); $user = new User([ - 'firstname' => $this->firstname, - 'lastname' => $this->lastname, + 'first_name' => $this->first_name, + 'last_name' => $this->last_name, 'email' => $this->email, 'email_verified_at' => now(), - 'password' => $this->password, 'role' => $this->role, ]); $user->saveOrFail(); @@ -106,14 +95,14 @@ class CreateAdmin extends Command */ private function addFirstname(): void { - while (is_null($this->firstname)) { + while (is_null($this->first_name)) { try { $tmp = $this->ask('Type the wanted user firstname', 'Admin'); $validator = Validator::make(['firstname' => $tmp], [ 'firstname' => 'required|min:3|max:255' ]); $validator->validate(); - $this->firstname = $tmp; + $this->first_name = $tmp; } catch (ValidationException $e) { $this->error(sprintf( 'Please provide a valid firstname %s', @@ -131,14 +120,14 @@ class CreateAdmin extends Command */ private function addLastname(): void { - while (is_null($this->lastname)) { + while (is_null($this->last_name)) { try { $tmp = $this->ask('Type the wanted user lastname', 'Saïtama'); $validator = Validator::make(['lastname' => $tmp], [ 'lastname' => 'required|min:3|max:255' ]); $validator->validate(); - $this->lastname = $tmp; + $this->last_name = $tmp; } catch (ValidationException $e) { $this->error(sprintf( 'Please provide a valid lastname %s', @@ -174,22 +163,6 @@ class CreateAdmin extends Command }; //end while } - /** - * Add a password to the user. - * - * @return void - */ - private function addPassword(): void - { - while ( - ($this->password = $this->secret( - (!\is_null($this->password) ? 'Failed to confirm, ' : '') . 'Type the wanted user password' - )) !== $this->secret('Confirm the password') - ) { - continue; - }; //end while - } - /** * Add a role to the user. * diff --git a/app/Http/Controllers/Bo/AppUserController.php b/app/Http/Controllers/Bo/AppUserController.php index fe6cb45..3f0d5c8 100644 --- a/app/Http/Controllers/Bo/AppUserController.php +++ b/app/Http/Controllers/Bo/AppUserController.php @@ -6,19 +6,26 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Bo\AppUser\StoreAppUserRequest; use App\Http\Requests\Bo\AppUser\UpdateAppUserRequest; use App\Http\Requests\Bo\ImportRequest; -use App\Http\Requests\Bo\UpdateChangeOrderRequest; use App\Imports\AppUserImport; use App\Models\AppUser; use App\Models\Directory; +use App\Traits\Controllers\ChangesModelOrder; use Illuminate\Http\Request; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\Session; -use Illuminate\Support\Str; use Maatwebsite\Excel\Facades\Excel; class AppUserController extends Controller { + use ChangesModelOrder; + + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(AppUser::class); + } + /** * Display a listing of the resource. * @@ -49,23 +56,27 @@ class AppUserController extends Controller /** Sort columns with a query */ $this->sortQuery($query); - $app_users = $this->paginate($query); + $appUserModels = $this->paginate($query); - return view('bo.pages.app-users.index', compact('app_users', 'searchFields')); + return view('bo.pages.app_users.index', compact('appUserModels', 'searchFields')); } /** * Show the form for creating a new resource. * + * @param \App\Models\AppUser $app_user * @return \Illuminate\View\View */ - public function create(): \Illuminate\View\View + public function create(AppUser $app_user): \Illuminate\View\View { $directories = Directory::renderDirectories( Directory::query()->where('needAuthorization', 1)->orderBy('name', 'ASC')->get() ); - return view('bo.pages.app-users.create', compact('directories')); + return view('bo.pages.app_users.create', [ + 'appUserModel' => $app_user, + 'directories' => $directories + ]); } /** @@ -83,10 +94,10 @@ class AppUserController extends Controller if ($app_user->saveOrFail()) { $app_user->directories()->sync(collect($request->directories)->pluck('id')); return redirect() - ->route('bo.app-users.edit', ['tenant' => \tenant(), 'app_user' => $app_user]) + ->route('bo.app_users.edit', ['tenant' => \tenant(), 'app_user' => $app_user]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); } @@ -94,7 +105,7 @@ class AppUserController extends Controller ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); }); } @@ -113,9 +124,9 @@ class AppUserController extends Controller // @phpstan-ignore assign.propertyReadOnly $app_user->directories = Directory::renderDirectories($app_user->directories); - return view('bo.pages.app-users.edit', [ - 'app_userModel' => $app_user, - 'directories' => $directories + return view('bo.pages.app_users.edit', [ + 'appUserModel' => $app_user, + 'directories' => $directories ]); } @@ -138,17 +149,17 @@ class AppUserController extends Controller if ($app_user->saveOrFail()) { return redirect() - ->route('bo.app-users.edit', ['tenant' => \tenant(), 'app_user' => $app_user]) + ->route('bo.app_users.edit', ['tenant' => \tenant(), 'app_user' => $app_user]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); }); } @@ -163,10 +174,10 @@ class AppUserController extends Controller { if ($app_user->deleteOrFail()) { return redirect() - ->route('bo.app-users.index', ['tenant' => \tenant()]) + ->route('bo.app_users.index', ['tenant' => \tenant()]) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); } @@ -174,10 +185,21 @@ class AppUserController extends Controller ->back() ->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.app_users'))] + ['model' => trans('models.classes.app_user')] )); } + /** + * Duplicate the specified resource. + * + * @param \App\Models\AppUser $app_user + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(AppUser $app_user): \Illuminate\Contracts\View\View + { + return $this->create($app_user->replicate()); + } + /** * Mass import by CSV. * matricule;lastname;firstname;email @@ -190,68 +212,7 @@ class AppUserController extends Controller { Excel::import(new AppUserImport(), $request->import, null, \Maatwebsite\Excel\Excel::CSV); - return redirect()->route('bo.app-users.index', ['tenant' => \tenant()])->with('success', 'Import terminé.'); - } - - /** - * Change order. - * - * @param \App\Http\Requests\Bo\UpdateChangeOrderRequest $request - * @param mixed ...$params - * @return \Illuminate\Http\RedirectResponse - * @throws \RuntimeException Si le nom du parametre de la route ne correspond pas au params utilisé. - */ - public function changeOrder(UpdateChangeOrderRequest $request, mixed ...$params): \Illuminate\Http\RedirectResponse - { - $paramNames = collect(request()->route()->parameterNames); - $className = Str::of($paramNames->get($paramNames->count() - 2))->lower()->studly(); - $className = "\\App\\Models\\AppUser"; - $modelId = Arr::get($params, $paramNames->count() - 2); - $model = AppUser::find($modelId); - - if (!class_exists($className)) { - throw new \RuntimeException("The {$className} class doesn't exist."); - } - - if (!\is_object($model)) { - throw new \RuntimeException("The second last param isn't an object."); - } - - if (get_class($model) !== (string) Str::of($className)->replaceStart('\\', '')) { - throw new \RuntimeException("The {$className} doesn't match second last binded param."); - } - - /** @var \Illuminate\Database\Eloquent\Model|null */ - $model = $className::query() - ->where('tenant_id', \tenant()) - ->where($model->getRouteKeyName(), $model->getRouteKey()) - ->first(); - - if (!$model) { - return back()->with('error', trans('crud.messages.order_not_changed')); - } - - return DB::transaction(function () use ($request, $className, $model) { - /** @var \Illuminate\Database\Eloquent\Model|null */ - $tmp = $className::query() - ->where('tenant_id', \tenant()) - ->where('order', $request->direction ? '<' : '>', $model->getOriginal('order')) - ->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first(); - - if (!$tmp) { - return back()->with('error', trans('crud.messages.order_not_changed')); - } - - $newOrder = $tmp->getOriginal('order'); - // @phpstan-ignore property.notFound - $tmp->order = $model->getOriginal('order'); - $tmp->saveQuietly(); - // @phpstan-ignore property.notFound - $model->order = $newOrder; - $model->saveQuietly(); - Session::flash('success', trans('crud.messages.order_changed')); - - return back(); - }); + return redirect()->route('bo.app_users.index', ['tenant' => \tenant()]) + ->with('success', 'Import terminé.'); } } diff --git a/app/Http/Controllers/Bo/ContactController.php b/app/Http/Controllers/Bo/ContactController.php index 51f4d66..285b760 100644 --- a/app/Http/Controllers/Bo/ContactController.php +++ b/app/Http/Controllers/Bo/ContactController.php @@ -11,6 +11,14 @@ use Illuminate\Database\Eloquent\Builder; class ContactController extends Controller { + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Contact::class); + } + /** * Display a listing of the resource. * @@ -49,11 +57,11 @@ class ContactController extends Controller }); } - $contactModelList = $this->paginate($query); + $contactModels = $this->paginate($query); $appUsers = AppUser::query()->select(['id', 'lastname', 'firstname'])->orderBy('lastname')->get(); - return view('bo.pages.contacts.index', compact('contactModelList', 'searchFields', 'appUsers')); + return view('bo.pages.contacts.index', compact('contactModels', 'searchFields', 'appUsers')); } /** diff --git a/app/Http/Controllers/Bo/DirectoriesController.php b/app/Http/Controllers/Bo/DirectoriesController.php index fd1bd0d..59fc783 100644 --- a/app/Http/Controllers/Bo/DirectoriesController.php +++ b/app/Http/Controllers/Bo/DirectoriesController.php @@ -9,10 +9,17 @@ use App\Models\Directory; use App\Models\Setting; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; class DirectoriesController extends Controller { + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Directory::class); + } + /** * Show the form for creating a new resource. * @@ -20,8 +27,8 @@ class DirectoriesController extends Controller */ public function index(): \Illuminate\Contracts\View\View { - $directories = $this->getDirectoriesForComponent(); - return \view('bo.pages.directories.index', \compact('directories')); + $directoryModels = $this->getDirectoriesForComponent(); + return \view('bo.pages.directories.index', \compact('directoryModels')); } /** @@ -40,14 +47,14 @@ class DirectoriesController extends Controller ->route('bo.directories.index', ['tenant' => \tenant()]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.directories'))] + ['model' => trans('models.classes.directory')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.directories'))] + ['model' => trans('models.classes.directory')] )); }); } @@ -60,7 +67,6 @@ class DirectoriesController extends Controller */ public function edit(Directory $directory): \Illuminate\View\View { - $this->authorize('edit', $directory); return view('bo.pages.directories.edit', ['directoryModel' => $directory]); } @@ -73,7 +79,6 @@ class DirectoriesController extends Controller */ public function update(UpdateDirectoryRequest $request, Directory $directory): \Illuminate\Http\RedirectResponse { - $this->authorize('edit', $directory); return DB::transaction(function () use ($request, $directory) { $directory->fill($request->validated()); @@ -82,14 +87,14 @@ class DirectoriesController extends Controller ->route('bo.directories.edit', ['tenant' => \tenant(), 'directory' => $directory]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.directories'))] + ['model' => trans('models.classes.directory')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.directories'))] + ['model' => trans('models.classes.directory')] )); }); } @@ -102,7 +107,6 @@ class DirectoriesController extends Controller */ public function destroy(Directory $directory): \Illuminate\Database\Eloquent\Collection { - $this->authorize('delete', $directory); return DB::transaction(function () use ($directory) { /** @var \App\Models\Directory $directoryChild */ foreach ($directory->children as $directoryChild) { @@ -126,7 +130,7 @@ class DirectoriesController extends Controller ':model :name n\'a pas été supprimé, un bouton est encore associé à ce dernier dans les paramètres :', [ - 'model' => Str::singular(trans('models.classes.directories')), + 'model' => trans('models.classes.directory'), 'name' => $directory->name['fr'] ] ) diff --git a/app/Http/Controllers/Bo/DocumentController.php b/app/Http/Controllers/Bo/DocumentController.php index f8e76c1..b2a8999 100644 --- a/app/Http/Controllers/Bo/DocumentController.php +++ b/app/Http/Controllers/Bo/DocumentController.php @@ -25,6 +25,14 @@ class DocumentController extends Controller { use ChangesModelOrder; + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Document::class); + } + /** * Display a listing of the resource. * @@ -57,28 +65,34 @@ class DocumentController extends Controller /** Sort columns with a query */ $this->sortQuery($query); - $documents = $this->paginate($query); + $documentModels = $this->paginate($query); return view('bo.pages.documents.index', [ - 'documents' => $documents, - 'searchFields' => $searchFields, - 'searchTag' => $request->tag, - 'tags' => $tags + 'documentModels' => $documentModels, + 'searchFields' => $searchFields, + 'searchTag' => $request->tag, + 'tags' => $tags ]); } /** * Show the form for creating a new resource. * + * @param \App\Models\Document $document * @return \Illuminate\View\View */ - public function create(): \Illuminate\View\View + public function create(Document $document): \Illuminate\View\View { - $documents = Document::where('lang', LangEnum::fr)->get(); - $directories = Directory::getChildrenDirectories(); - $tags = Tag::get(); + $documentsList = Document::query()->where('lang', LangEnum::fr)->get(); + $directories = Directory::getChildrenDirectories(); + $tags = Tag::get(); - return view('bo.pages.documents.create', compact('documents', 'directories', 'tags')); + return view('bo.pages.documents.create', [ + 'documentModel' => $document, + 'documentsList' => $documentsList, + 'directories' => $directories, + 'tags' => $tags + ]); } /** @@ -98,14 +112,14 @@ class DocumentController extends Controller ->route('bo.documents.edit', ['tenant' => \tenant(), 'document' => $document]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); }); } @@ -118,13 +132,13 @@ class DocumentController extends Controller */ public function edit(Document $document): \Illuminate\View\View { - $documents = Document::where('lang', LangEnum::fr)->get(); - $directories = Directory::getChildrenDirectories(); - $tags = Tag::get(); + $documentsList = Document::query()->where('lang', LangEnum::fr)->get(); + $directories = Directory::getChildrenDirectories(); + $tags = Tag::get(); return view('bo.pages.documents.edit', [ - 'documents' => $documents, 'documentModel' => $document, + 'documentsList' => $documentsList, 'directories' => $directories, 'tags' => $tags ]); @@ -147,14 +161,14 @@ class DocumentController extends Controller ->route('bo.documents.edit', ['tenant' => \tenant(), 'document' => $document]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); }); } @@ -181,14 +195,14 @@ class DocumentController extends Controller ->route('bo.documents.index', array_merge(['tenant' => \tenant()], $previousQueries)) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.documents'))] + ['model' => trans('models.classes.document')] )); } @@ -201,7 +215,7 @@ class DocumentController extends Controller public function download(string $uuid): \Symfony\Component\HttpFoundation\BinaryFileResponse { $doc = Document::where('uuid', $uuid)->firstOrFail(); - $pathToFile = public_path() . Storage::url(sprintf("docs/%s.%s", $doc->uuid, $doc->type->name())); + $pathToFile = public_path() . Storage::url(sprintf("docs/%s.%s", $doc->uuid, $doc->type)); return response()->download($pathToFile, $doc->path); } diff --git a/app/Http/Controllers/Bo/FormularyController.php b/app/Http/Controllers/Bo/FormularyController.php index 6297424..4e9462a 100644 --- a/app/Http/Controllers/Bo/FormularyController.php +++ b/app/Http/Controllers/Bo/FormularyController.php @@ -2,7 +2,6 @@ namespace App\Http\Controllers\Bo; -use App\Enums\Formularies\InputStatusEnum; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Formularies\StoreFormularyRequest; use App\Http\Requests\Bo\Formularies\UpdateFormularyRequest; @@ -11,12 +10,19 @@ use App\Models\Formulary; use App\Traits\Controllers\ChangesModelOrder; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; class FormularyController extends Controller { use ChangesModelOrder; + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Formulary::class); + } + /** * Display a listing of the resource. * @@ -41,19 +47,20 @@ class FormularyController extends Controller /** Sort columns with a query */ $this->sortQuery($query); - $formulariesModel = $this->paginate($query); + $formularyModels = $this->paginate($query); - return view('bo.pages.formularies.index', compact('formulariesModel', 'searchFields')); + return view('bo.pages.formularies.index', compact('formularyModels', 'searchFields')); } /** * Show the form for creating a new resource. * + * @param \App\Models\Formulary $formulary * @return \Illuminate\View\View */ - public function create(): \Illuminate\View\View + public function create(Formulary $formulary): \Illuminate\View\View { - return view('bo.pages.formularies.create'); + return view('bo.pages.formularies.create', ['formularyModel' => $formulary]); } /** @@ -73,14 +80,14 @@ class FormularyController extends Controller ->route('bo.formularies.edit', ['tenant' => \tenant(), 'formulary' => $formulary]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); }); } @@ -93,17 +100,9 @@ class FormularyController extends Controller */ public function edit(Formulary $formulary): \Illuminate\Http\RedirectResponse|\Illuminate\Contracts\View\View { - if ($formulary->status->equals(InputStatusEnum::system)) { - return redirect()->route('bo.formularies.index') - ->with('error', trans( - 'Administration du champs :name impossible car il est vérouillé !', - ['name' => $formulary->name] - )); - } else { - return view('bo.pages.formularies.edit', [ - 'formularyModel' => $formulary - ]); - } + return view('bo.pages.formularies.edit', [ + 'formularyModel' => $formulary + ]); } /** @@ -123,14 +122,14 @@ class FormularyController extends Controller ->route('bo.formularies.edit', ['tenant' => \tenant(), 'formulary' => $formulary]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); }); } @@ -154,14 +153,25 @@ class FormularyController extends Controller ->route('bo.formularies.index', array_merge(['tenant' => \tenant()], $previousQueries)) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); } return redirect()->back()->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.formularies'))] + ['model' => trans('models.classes.formulary')] )); }); } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\Formulary $formulary + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(Formulary $formulary): \Illuminate\Contracts\View\View + { + return $this->create($formulary->replicate()); + } } diff --git a/app/Http/Controllers/Bo/SettingController.php b/app/Http/Controllers/Bo/SettingController.php index d00bad2..4e1c58d 100644 --- a/app/Http/Controllers/Bo/SettingController.php +++ b/app/Http/Controllers/Bo/SettingController.php @@ -6,27 +6,47 @@ use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Settings\UpdateSettingRequest; use App\Models\Directory; use App\Models\Setting; +use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; class SettingController extends Controller { + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Setting::class); + } + /** * Display a listing of the resource. * + * @param \Illuminate\Http\Request $request * @return \Illuminate\Contracts\View\View */ - public function index(): \Illuminate\Contracts\View\View + public function index(Request $request): \Illuminate\Contracts\View\View { $directories = Directory::renderDirectories(Directory::query()->whereNull('directory_id')->get()); - /** @var \Illuminate\Database\Eloquent\Builder $settings */ - $settings = Setting::query(); + /** @var \Illuminate\Database\Eloquent\Builder $query */ + $query = Setting::query(); - $this->sortQuery($settings); - $settings = $settings->get(); + if ($request->search) { + $this->searchQuery( + $query, + $request->search, + null, + 'key', + ); + } + $searchFields = trans('validation.custom.key'); - return view('bo.pages.settings.index', compact('settings', 'directories')); + $this->sortQuery($query); + + $settingModels = $query->get(); + + return view('bo.pages.settings.index', compact('settingModels', 'searchFields', 'directories')); } /** @@ -58,14 +78,14 @@ class SettingController extends Controller ->route('bo.settings.edit', ['tenant' => \tenant(), 'setting' => $setting]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.settings'))] + ['model' => trans('models.classes.setting')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.settings'))] + ['model' => trans('models.classes.setting')] )); }); } @@ -81,17 +101,17 @@ class SettingController extends Controller return DB::transaction(function () use ($setting) { if ($setting->deleteOrFail()) { return redirect() - ->route('bo.settings.index') + ->route('bo.settings.index', ['tenant' => \tenant()]) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.settings'))] + ['model' => trans('models.classes.setting')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.settings'))] + ['model' => trans('models.classes.setting')] )); }); } diff --git a/app/Http/Controllers/Bo/TagController.php b/app/Http/Controllers/Bo/TagController.php index 1b03233..f89009c 100644 --- a/app/Http/Controllers/Bo/TagController.php +++ b/app/Http/Controllers/Bo/TagController.php @@ -9,12 +9,19 @@ use App\Models\Tag; use App\Traits\Controllers\ChangesModelOrder; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; class TagController extends Controller { use ChangesModelOrder; + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Tag::class); + } + /** * Display a listing of the resource. * @@ -39,19 +46,20 @@ class TagController extends Controller /** Sort columns with a query */ $this->sortQuery($query); - $tags = $this->paginate($query); + $tagModels = $this->paginate($query); - return view('bo.pages.tags.index', compact('tags', 'searchFields')); + return view('bo.pages.tags.index', compact('tagModels', 'searchFields')); } /** * Show the form for creating a new resource. * + * @param \App\Models\Tag $tag * @return \Illuminate\Contracts\View\View */ - public function create(): \Illuminate\Contracts\View\View + public function create(Tag $tag): \Illuminate\Contracts\View\View { - return view('bo.pages.tags.create'); + return view('bo.pages.tags.create', ['tagModel' => $tag]); } /** @@ -70,14 +78,14 @@ class TagController extends Controller ->route('bo.tags.edit', ['tenant' => \tenant(), 'tag' => $tag]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); }); } @@ -110,14 +118,14 @@ class TagController extends Controller ->route('bo.tags.edit', ['tenant' => \tenant(), 'tag' => $tag]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); }); } @@ -139,15 +147,26 @@ class TagController extends Controller ->route('bo.tags.index', array_merge(['tenant' => \tenant()], $previousQueries)) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.tags'))] + ['model' => trans('models.classes.tag')] )); }); } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\Tag $tag + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(Tag $tag): \Illuminate\Contracts\View\View + { + return $this->create($tag->replicate()); + } } diff --git a/app/Http/Controllers/Bo/TenantController.php b/app/Http/Controllers/Bo/TenantController.php index d74242b..2a1a43b 100644 --- a/app/Http/Controllers/Bo/TenantController.php +++ b/app/Http/Controllers/Bo/TenantController.php @@ -3,14 +3,22 @@ namespace App\Http\Controllers\Bo; use App\Http\Controllers\Controller; -use App\Http\Requests\Bo\Tenants\TenantRequest; +use App\Http\Requests\Bo\Tenants\StoreTenantRequest; +use App\Http\Requests\Bo\Tenants\UpdateTenantRequest; use App\Models\Tenant; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Str; class TenantController extends Controller { + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(Tenant::class); + } + /** * Display a listing of the resource. * @@ -35,28 +43,29 @@ class TenantController extends Controller /** Sort columns with a query */ $this->sortQuery($query); - $tenants = $this->paginate($query); + $tenantModels = $this->paginate($query); - return view('bo.pages.tenants.index', compact('tenants', 'searchFields')); + return view('bo.pages.tenants.index', compact('tenantModels', 'searchFields')); } /** * Show the form for creating a new resource. * + * @param \App\Models\Tenant $tenant * @return \Illuminate\Contracts\View\View */ - public function create(): \Illuminate\Contracts\View\View + public function create(Tenant $tenant): \Illuminate\Contracts\View\View { - return view('bo.pages.tenants.create'); + return view('bo.pages.tenants.create', ['tenantModel' => $tenant]); } /** * Store a newly created resource in storage. * - * @param \App\Http\Requests\Bo\Tenants\TenantRequest $request + * @param \App\Http\Requests\Bo\Tenants\StoreTenantRequest $request * @return \Illuminate\Http\RedirectResponse */ - public function store(TenantRequest $request): \Illuminate\Http\RedirectResponse + public function store(StoreTenantRequest $request): \Illuminate\Http\RedirectResponse { return DB::transaction(function () use ($request) { $tenant = new Tenant(); @@ -66,14 +75,14 @@ class TenantController extends Controller ->route('bo.tenants.edit', ['tenant' => $tenant]) ->with('success', trans( ':model a été créé', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); }); } @@ -92,11 +101,11 @@ class TenantController extends Controller /** * Update the specified resource in storage. * - * @param \App\Http\Requests\Bo\Tenants\TenantRequest $request - * @param \App\Models\Tenant $tenant + * @param \App\Http\Requests\Bo\Tenants\UpdateTenantRequest $request + * @param \App\Models\Tenant $tenant * @return \Illuminate\Http\RedirectResponse */ - public function update(TenantRequest $request, Tenant $tenant): \Illuminate\Http\RedirectResponse + public function update(UpdateTenantRequest $request, Tenant $tenant): \Illuminate\Http\RedirectResponse { return DB::transaction(function () use ($request, $tenant) { $tenant->fill($request->validated()); @@ -106,14 +115,14 @@ class TenantController extends Controller ->route('bo.tenants.edit', ['tenant' => $tenant]) ->with('success', trans( ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); }); } @@ -132,18 +141,29 @@ class TenantController extends Controller return DB::transaction(function () use ($tenant, $previousQueries) { if ($tenant->deleteOrFail()) { return redirect() - ->route('bo.tenants.index', array_merge(['tenant' => \tenant()], $previousQueries)) + ->route('bo.tenants.index', $previousQueries) ->with('warning', trans( ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); } return redirect() ->back() ->with('error', trans( ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.tenants'))] + ['model' => trans('models.classes.tenant')] )); }); } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\Tenant $tenant + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(Tenant $tenant): \Illuminate\Contracts\View\View + { + return $this->create($tenant->replicate()); + } } diff --git a/app/Http/Controllers/Bo/UserController.php b/app/Http/Controllers/Bo/UserController.php index 1a7213e..5d92703 100644 --- a/app/Http/Controllers/Bo/UserController.php +++ b/app/Http/Controllers/Bo/UserController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Bo; use App\Http\Controllers\Controller; use App\Http\Requests\Bo\Users\StoreUserRequest; use App\Http\Requests\Bo\Users\UpdateUserRequest; +use App\Models\Tenant; use App\Models\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -12,6 +13,14 @@ use Illuminate\Support\Str; class UserController extends Controller { + /** + * Create the controller instance. + */ + public function __construct() + { + $this->authorizeResource(User::class); + } + /** * Display a listing of the resource. * @@ -20,35 +29,48 @@ class UserController extends Controller */ public function index(Request $request): \Illuminate\Contracts\View\View { - /** @var \Illuminate\Database\Eloquent\Builder $query */ - $query = User::query(); + /** @var \Illuminate\Database\Eloquent\Builder $userModels */ + $userModels = User::query(); - if ($request->search) { + /** @var string $search Search field */ + $search = $request->search; + if ($search) { $this->searchQuery( - $query, - $request->search, + $userModels, + $search, null, - 'name', + 'first_name', + 'last_name', + 'email', + 'role', ); } - $searchFields = trans('validation.attributes.name'); + $searchFields = \implode(', ', [ + trans('validation.attributes.first_name'), + trans('validation.attributes.last_name'), + trans('validation.attributes.email'), + trans('validation.attributes.role'), + ]); /** Sort columns with a query */ - $this->sortQuery($query); + $this->sortQuery($userModels); - $users = $this->paginate($query); + /** Custom pagination */ + $userModels = $this->paginate($userModels); - return view('bo.pages.users.index', compact('users', 'searchFields')); + return view('bo.pages.users.index', compact('userModels', 'search', 'searchFields')); } /** * Show the form for creating a new resource. * + * @param \App\Models\User $user * @return \Illuminate\Contracts\View\View */ - public function create(): \Illuminate\Contracts\View\View + public function create(User $user): \Illuminate\Contracts\View\View { - return view('bo.pages.users.create'); + $tenants = Tenant::query()->orderBy('name', 'ASC')->get(); + return view('bo.pages.users.create', ['userModel' => $user, 'tenants' => $tenants]); } /** @@ -62,20 +84,18 @@ class UserController extends Controller return DB::transaction(function () use ($request) { $user = new User(); $user->fill($request->validated()); + if ($user->saveOrFail()) { - return redirect() - ->route('bo.users.edit', ['tenant' => \tenant(), 'user' => $user]) - ->with('success', trans( - ':model a été créé', - ['model' => Str::singular(trans('models.classes.users'))] - )); + $user->tenants()->sync(array_column($request->validated()['tenants'], 'id')); + return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $user]) + ->with('success', __('crud.messages.has_been_created', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); } - return redirect() - ->back() - ->with('error', trans( - ':model n\'a pas été créé', - ['model' => Str::singular(trans('models.classes.users'))] - )); + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_created', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); }); } @@ -87,7 +107,8 @@ class UserController extends Controller */ public function edit(User $user): \Illuminate\Contracts\View\View { - return view('bo.pages.users.edit', ['userModel' => $user]); + $tenants = Tenant::query()->orderBy('name', 'ASC')->get(); + return view('bo.pages.users.edit', ['userModel' => $user, 'tenants' => $tenants]); } /** @@ -103,19 +124,16 @@ class UserController extends Controller $user->fill($request->validated()); if ($user->saveOrFail()) { - return redirect() - ->route('bo.users.edit', ['tenant' => \tenant(), 'user' => $user]) - ->with('success', trans( - ':model a été mis à jour', - ['model' => Str::singular(trans('models.classes.users'))] - )); + $user->tenants()->sync(array_column($request->validated()['tenants'], 'id')); + return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $user]) + ->with('success', __('crud.messages.has_been_updated', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); } - return redirect() - ->back() - ->with('error', trans( - ':model n\'a pas été mis à jour', - ['model' => Str::singular(trans('models.classes.users'))] - )); + return redirect()->route('bo.users.edit', ['tenant' => tenant(), 'user' => $user]) + ->with('error', __('crud.messages.cannot_be_updated', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); }); } @@ -127,24 +145,26 @@ class UserController extends Controller */ public function destroy(User $user): \Illuminate\Http\RedirectResponse { - /** @var array */ - $previousQueries = []; - \parse_str(\parse_url(url()->previous(), \PHP_URL_QUERY), $previousQueries); - return DB::transaction(function () use ($user, $previousQueries) { - if ($user->deleteOrFail()) { - return redirect() - ->route('bo.users.index', array_merge(['tenant' => \tenant(), 'user' => $user], $previousQueries)) - ->with('warning', trans( - ':model a bien été supprimé', - ['model' => Str::singular(trans('models.classes.users'))] - )); - } - return redirect() - ->back() - ->with('error', trans( - ':model n\'a pas été supprimé', - ['model' => Str::singular(trans('models.classes.users'))] - )); - }); + if ($user->deleteOrFail()) { + return redirect()->route('bo.users.index', ['tenant' => tenant()]) + ->with('success', __('crud.messages.has_been_deleted', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + return redirect()->back() + ->with('error', __('crud.messages.cannot_be_deleted', [ + 'model' => Str::ucfirst(__('models.classes.user')) + ])); + } + + /** + * Duplicate the specified resource. + * + * @param \App\Models\User $user + * @return \Illuminate\Contracts\View\View + */ + public function duplicate(User $user): \Illuminate\Contracts\View\View + { + return $this->create($user->replicate()); } } diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 6ca6f66..2d87411 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -2,9 +2,7 @@ namespace App\Http\Controllers; -use App\Enums\UserRolesEnum; use App\Lib\Helpers\ToolboxHelper; -use App\Models\Tenant; use Illuminate\Database\Eloquent\Builder; use Illuminate\Foundation\Auth\Access\AuthorizesRequests; use Illuminate\Foundation\Bus\DispatchesJobs; @@ -166,20 +164,4 @@ class Controller extends BaseController $query = $query->paginate($pagination); return $query; } - - /** - * Redirect with default tenant. - * - * @return \Illuminate\Http\RedirectResponse - */ - public function redirectWithDefaultTenant(): \Illuminate\Http\RedirectResponse - { - $user = auth('backend')->user(); - $tenant = UserRolesEnum::admin->equals($user->role) ? - Tenant::first()->getKey() : - $user->tenants->first()->getKey(); - return redirect()->route('bo.dashboard', [ - 'tenant' => $tenant - ]); - } } diff --git a/app/Http/Middleware/RedirectIfAuthenticated.php b/app/Http/Middleware/RedirectIfAuthenticated.php index fd01417..81cdb80 100644 --- a/app/Http/Middleware/RedirectIfAuthenticated.php +++ b/app/Http/Middleware/RedirectIfAuthenticated.php @@ -33,7 +33,7 @@ class RedirectIfAuthenticated case 'frontend': return \redirect()->route('home'); case 'backend': - return \redirect()->route('bo.dashboard', ['tenant' => tenant()]); + return \redirect()->route('bo.dashboard'); default: throw new \RuntimeException("Unhandled guard `{$guard}` ."); } diff --git a/app/Http/Middleware/RestrictTenantAccess.php b/app/Http/Middleware/RestrictTenantAccess.php index d6eb889..1e2a6e2 100644 --- a/app/Http/Middleware/RestrictTenantAccess.php +++ b/app/Http/Middleware/RestrictTenantAccess.php @@ -2,7 +2,6 @@ namespace App\Http\Middleware; -use App\Enums\UserRolesEnum; use App\Models\Tenant; use Closure; use Illuminate\Http\Request; @@ -19,19 +18,36 @@ class RestrictTenantAccess */ public function handle(Request $request, Closure $next): \Symfony\Component\HttpFoundation\Response { + /** @var \App\Models\User|null $user */ $user = \auth('backend')->user(); if ($user) { - if ($request->routeIs('bo.tenants.*')) { - tenancy()->initialize(Tenant::firstOrFail()); + if ( + !$request->routeIs('bo.dashboard') && + !$request->routeIs('bo.users.*') && + !$request->routeIs('bo.tenants.*') && + $request->tenant && + !\in_array($request->tenant, Tenant::get()->pluck('id')->toArray()) + ) { + // @phpstan-ignore argument.type + abort('404'); + } + if ( + (Tenant::count() && $request->routeIs('bo.tenants.*') && $user->can('isAdmin')) || + (Tenant::count() && $request->routeIs('bo.users.*') && $user->can('isAdmin')) + ) { + $tenant = Tenant::first()->getKey(); + tenancy()->initialize($tenant); return $next($request); } if ( - !\in_array($request->tenant, $user->tenants->pluck('id')->toArray()) && - !(UserRolesEnum::admin->equals($user->role)) + Tenant::count() && ($request->routeIs('bo.dashboard') + || $request->routeIs('bo.users.edit', $user->getKey())) ) { - abort(403); + $tenant = ($user->tenants->count()) ? $user->tenants->first() : Tenant::first()->getKey(); + tenancy()->initialize($tenant); + return $next($request); } - } + } //end if return $next($request); } } diff --git a/app/Http/Requests/Api/SendFlyerRequest.php b/app/Http/Requests/Api/SendFlyerRequest.php index 6a5795c..cf432c7 100644 --- a/app/Http/Requests/Api/SendFlyerRequest.php +++ b/app/Http/Requests/Api/SendFlyerRequest.php @@ -41,7 +41,7 @@ class SendFlyerRequest extends FormRequest 'email' => trans('validation.attributes.email'), 'subject' => trans('validation.attributes.subject'), 'message' => trans('validation.attributes.message'), - 'app_user_id' => trans('models.classes.app_users'), + 'app_user_id' => trans('models.classes.app_user'), ]; } } diff --git a/app/Http/Requests/Bo/AppUser/StoreAppUserRequest.php b/app/Http/Requests/Bo/AppUser/StoreAppUserRequest.php index 2c06be9..fccb312 100644 --- a/app/Http/Requests/Bo/AppUser/StoreAppUserRequest.php +++ b/app/Http/Requests/Bo/AppUser/StoreAppUserRequest.php @@ -3,7 +3,10 @@ namespace App\Http\Requests\Bo\AppUser; use App\Enums\AppUserRolesEnum; +use App\Models\AppUser; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Gate; +use Illuminate\Support\Str; use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Enum; use Illuminate\Validation\Rules\Password; @@ -17,7 +20,7 @@ class StoreAppUserRequest extends FormRequest */ public function authorize(): bool { - return auth('backend')->check(); + return Gate::check('create', AppUser::class); } /** @@ -76,13 +79,13 @@ class StoreAppUserRequest extends FormRequest public function attributes(): array { return [ - 'lastname' => \trans('nom'), - 'firstname' => \trans('prénom'), - 'email' => \trans('email'), - 'password' => \trans('mot de passe'), - 'authorizedFolders' => \trans('dossiers autorisés'), - 'role' => \trans('rôle'), - 'status' => \trans('statut') + 'lastname' => \trans('validation.attributes.last_name'), + 'firstname' => \trans('validation.attributes.first_name'), + 'email' => \trans('validation.attributes.email'), + 'password' => \trans('validation.attributes.password'), + 'authorizedFolders' => Str::of(\trans('models.classes.directory'))->plural(), + 'role' => \trans('validation.custom.role'), + 'status' => \trans('validation.custom.status') ]; } } diff --git a/app/Http/Requests/Bo/AppUser/UpdateAppUserRequest.php b/app/Http/Requests/Bo/AppUser/UpdateAppUserRequest.php index 904511d..3a4be8a 100644 --- a/app/Http/Requests/Bo/AppUser/UpdateAppUserRequest.php +++ b/app/Http/Requests/Bo/AppUser/UpdateAppUserRequest.php @@ -2,11 +2,22 @@ namespace App\Http\Requests\Bo\AppUser; +use Illuminate\Support\Facades\Gate; use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Password; class UpdateAppUserRequest extends StoreAppUserRequest { + /** + * Determine if the user is authorized to make this request. + * + * @return boolean + */ + public function authorize(): bool + { + return Gate::check('update', $this->route('app_user')); + } + /** * Get the validation rules that apply to the request. * diff --git a/app/Http/Requests/Bo/Directories/StoreDirectoryRequest.php b/app/Http/Requests/Bo/Directories/StoreDirectoryRequest.php index db70a2c..1514595 100644 --- a/app/Http/Requests/Bo/Directories/StoreDirectoryRequest.php +++ b/app/Http/Requests/Bo/Directories/StoreDirectoryRequest.php @@ -3,7 +3,9 @@ namespace App\Http\Requests\Bo\Directories; use App\Enums\Documents\LangEnum; +use App\Models\Directory; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Gate; class StoreDirectoryRequest extends FormRequest { @@ -14,7 +16,7 @@ class StoreDirectoryRequest extends FormRequest */ public function authorize(): bool { - return auth('backend')->check(); + return Gate::check('create', Directory::class); } /** @@ -51,14 +53,12 @@ class StoreDirectoryRequest extends FormRequest public function attributes(): array { $rules = [ - 'name' => \trans('nom du dossier'), - 'needAuthorization' => \trans('autorisation du dossier') + 'name' => \trans('validation.attributes.name'), + 'needAuthorization' => \trans('validation.custom.authorization') ]; /** @var Object $langEnum */ foreach (LangEnum::toArray() as $langEnum) { - $rules["name.{$langEnum->name}"] = \trans('nom du dossier :lang', [ - 'lang' => $langEnum->label - ]); + $rules["name.{$langEnum->name}"] = sprintf('%s %s', trans('validation.attributes.name'), $langEnum->label); } return $rules; } diff --git a/app/Http/Requests/Bo/Directories/UpdateDirectoryRequest.php b/app/Http/Requests/Bo/Directories/UpdateDirectoryRequest.php index 2461e63..69bcf71 100644 --- a/app/Http/Requests/Bo/Directories/UpdateDirectoryRequest.php +++ b/app/Http/Requests/Bo/Directories/UpdateDirectoryRequest.php @@ -2,6 +2,17 @@ namespace App\Http\Requests\Bo\Directories; +use Illuminate\Support\Facades\Gate; + class UpdateDirectoryRequest extends StoreDirectoryRequest { + /** + * Determine if the user is authorized to make this request. + * + * @return boolean + */ + public function authorize(): bool + { + return Gate::check('update', $this->route('directory')); + } } diff --git a/app/Http/Requests/Bo/Documents/StoreDocumentRequest.php b/app/Http/Requests/Bo/Documents/StoreDocumentRequest.php index 3ec7b08..065ac43 100644 --- a/app/Http/Requests/Bo/Documents/StoreDocumentRequest.php +++ b/app/Http/Requests/Bo/Documents/StoreDocumentRequest.php @@ -7,7 +7,9 @@ use App\Enums\Documents\TypeEnum; use App\Lib\Helpers\FileStorageHelper; use App\Models\Document; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Facades\Storage; +use Illuminate\Validation\Rule; use Illuminate\Validation\Rules\Enum; class StoreDocumentRequest extends FormRequest @@ -19,7 +21,7 @@ class StoreDocumentRequest extends FormRequest */ public function authorize(): bool { - return auth('backend')->check(); + return Gate::check('create', Document::class); } /** @@ -30,7 +32,7 @@ class StoreDocumentRequest extends FormRequest protected function prepareForValidation(): void { $this->merge([ - 'status' => $this->status ? true : false + 'status' => $this->status ? true : false, ]); // * Sanitize Path if (!empty($this->path)) { @@ -66,10 +68,12 @@ class StoreDocumentRequest extends FormRequest 'directory_id' => 'required|integer', 'document_id' => 'sometimes:directory_id|nullable|integer', 'uuid' => [ + 'required', + 'uuid', // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed - 'required', 'uuid', function ($attribute, $value, $fail) use ($disk) { - if ($this->type and \in_array($this->type, TypeEnum::toValues())) { - $extension = TypeEnum::make($this->type, 'value')->name(); + function ($attribute, $value, $fail) use ($disk) { + if ($this->type and \array_column(TypeEnum::toArray(), 'name')) { + $extension = TypeEnum::make($this->type, 'name')->name(); $value = "{$value}.{$extension}"; } if (!$disk->has($value)) { @@ -81,15 +85,9 @@ class StoreDocumentRequest extends FormRequest } ], 'title' => 'required|min:3|max:255', - 'type' => [ - 'required', - new Enum(TypeEnum::class) - ], + 'type' => ['required', 'string', Rule::in(array_column(TypeEnum::toArray(), 'name'))], 'path' => 'required|min:3|max:255', - 'lang' => [ - 'required', - new Enum(LangEnum::class) - ], + 'lang' => ['required', new Enum(LangEnum::class)], 'status' => 'required|boolean' ]; } @@ -102,14 +100,14 @@ class StoreDocumentRequest extends FormRequest public function attributes(): array { return [ - 'directory_id' => \trans('dossier à associer'), - 'document_id' => \trans('document (FR) à associer'), - 'title' => \trans('titre'), - 'uuid' => \trans('document'), - 'type' => \trans('type du document'), - 'path' => \trans('chemin du document'), - 'lang' => \trans('langue du document'), - 'status' => \trans('état de publication du document'), + 'directory_id' => \trans('models.classes.directory'), + 'document_id' => \trans('models.classes.document'), + 'title' => \trans('validation.attributes.title'), + 'uuid' => \trans('models.classes.document'), + 'type' => \trans('validation.custom.type'), + 'path' => \trans('validation.custom.path'), + 'lang' => \trans('validation.custom.lang'), + 'status' => \trans('validation.custom.status'), ]; } } diff --git a/app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php b/app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php index 54186f4..4ea924e 100644 --- a/app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php +++ b/app/Http/Requests/Bo/Documents/UpdateDocumentRequest.php @@ -2,6 +2,17 @@ namespace App\Http\Requests\Bo\Documents; +use Illuminate\Support\Facades\Gate; + class UpdateDocumentRequest extends StoreDocumentRequest { + /** + * Determine if the user is authorized to make this request. + * + * @return boolean + */ + public function authorize(): bool + { + return Gate::check('update', $this->route('document')); + } } diff --git a/app/Http/Requests/Bo/FilterCustomerRequest.php b/app/Http/Requests/Bo/FilterCustomerRequest.php index 74bdc70..e4770c6 100644 --- a/app/Http/Requests/Bo/FilterCustomerRequest.php +++ b/app/Http/Requests/Bo/FilterCustomerRequest.php @@ -14,7 +14,7 @@ class FilterCustomerRequest extends FormRequest */ public function authorize(): bool { - return \auth()->check(); + return \auth('backend')->check(); } /** diff --git a/app/Http/Requests/Bo/Formularies/StoreFormularyRequest.php b/app/Http/Requests/Bo/Formularies/StoreFormularyRequest.php index 98a1b20..dbf5cb6 100644 --- a/app/Http/Requests/Bo/Formularies/StoreFormularyRequest.php +++ b/app/Http/Requests/Bo/Formularies/StoreFormularyRequest.php @@ -4,7 +4,9 @@ namespace App\Http\Requests\Bo\Formularies; use App\Enums\Formularies\InputStatusEnum; use App\Enums\Formularies\InputTypeEnum; +use App\Models\Formulary; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Gate; use Illuminate\Validation\Rules\Enum; class StoreFormularyRequest extends FormRequest @@ -15,6 +17,7 @@ class StoreFormularyRequest extends FormRequest * @var string[] */ private $mobileAppLocales = ['fr', 'en', 'de', 'nl']; + /** * Determine if the user is authorized to make this request. * @@ -22,7 +25,7 @@ class StoreFormularyRequest extends FormRequest */ public function authorize(): bool { - return auth('backend')->check(); + return Gate::check('create', Formulary::class); } /** @@ -71,14 +74,14 @@ class StoreFormularyRequest extends FormRequest public function attributes(): array { $attributes = [ - 'type' => trans('type'), - 'name' => trans('nom'), - 'placeholder' => trans('placeholder'), - 'details' => trans('détail'), - 'details.*' => trans('détail 2'), - 'details.*.value' => trans('valeur d\'un élément dans la liste des détails du contenu'), - 'required' => trans('l\'état d\'obligation'), - 'is_visible' => trans('l\'état de visibilité'), + 'type' => trans('validation.custom.type'), + 'name' => trans('validation.attributes.name'), + 'placeholder' => trans('validation.custom.placeholder'), + 'details' => trans('validation.custom.detail'), + 'details.*' => trans('validation.custom.detail'), + 'details.*.value' => trans('validation.custom.valeur'), + 'required' => trans('validation.custom.required'), + 'is_visible' => trans('validation.custom.visibility'), ]; foreach ($this->mobileAppLocales as $locale) { $attributes["placeholder.{$locale}"] = trans('placeholder (:lang)', ['lang' => $locale]); diff --git a/app/Http/Requests/Bo/Formularies/UpdateFormularyRequest.php b/app/Http/Requests/Bo/Formularies/UpdateFormularyRequest.php index 185183a..0bdc4ee 100644 --- a/app/Http/Requests/Bo/Formularies/UpdateFormularyRequest.php +++ b/app/Http/Requests/Bo/Formularies/UpdateFormularyRequest.php @@ -2,8 +2,20 @@ namespace App\Http\Requests\Bo\Formularies; +use Illuminate\Support\Facades\Gate; + class UpdateFormularyRequest extends StoreFormularyRequest { + /** + * Determine if the user is authorized to make this request. + * + * @return boolean + */ + public function authorize(): bool + { + return Gate::check('update', $this->route('formulary')); + } + /** * Get the validation rules that apply to the request. * diff --git a/app/Http/Requests/Bo/Settings/UpdateSettingRequest.php b/app/Http/Requests/Bo/Settings/UpdateSettingRequest.php index db1c523..6e5be30 100644 --- a/app/Http/Requests/Bo/Settings/UpdateSettingRequest.php +++ b/app/Http/Requests/Bo/Settings/UpdateSettingRequest.php @@ -2,11 +2,11 @@ namespace App\Http\Requests\Bo\Settings; -use App\Enums\UserRolesEnum; use App\Models\Setting; use App\Traits\Requests\HasPicture; use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Facades\Gate; class UpdateSettingRequest extends FormRequest { @@ -25,9 +25,11 @@ class UpdateSettingRequest extends FormRequest $this->key === "Button3" || $this->key === "ShowIsActive" ) { - return auth('backend')->check(); + return Gate::check('update', $this->route('setting')); } else { - return (auth('backend')->check() && Auth::user()->role->value() === UserRolesEnum::admin->value()); + /** @var \App\Models\User $user */ + $user = Auth::guard('backend')->user(); + return $user->can('isAdmin'); } } @@ -70,9 +72,9 @@ class UpdateSettingRequest extends FormRequest ) { $buttonsNullable = 0; $buttons = Setting::where('key', 'Button1') - ->orWhere('key', 'Button2') - ->orWhere('key', 'Button3') - ->get(); + ->orWhere('key', 'Button2') + ->orWhere('key', 'Button3') + ->get(); foreach ($buttons as $button) { if ($button->value === null) { if ($button->key !== $this->key) { @@ -110,9 +112,9 @@ class UpdateSettingRequest extends FormRequest public function attributes(): array { return [ - 'key' => \trans('clé'), - 'value' => \trans('valeur'), - 'type' => \trans('type'), + 'key' => \trans('validation.custom.key'), + 'value' => \trans('validation.custom.value'), + 'type' => \trans('validation.custom.type'), ]; } } diff --git a/app/Http/Requests/Bo/Tags/StoreTagRequest.php b/app/Http/Requests/Bo/Tags/StoreTagRequest.php index a2a0058..7910cff 100644 --- a/app/Http/Requests/Bo/Tags/StoreTagRequest.php +++ b/app/Http/Requests/Bo/Tags/StoreTagRequest.php @@ -2,8 +2,11 @@ namespace App\Http\Requests\Bo\Tags; +use App\Models\Tag; use Illuminate\Foundation\Http\FormRequest; +use Illuminate\Support\Facades\Gate; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; class StoreTagRequest extends FormRequest { @@ -14,7 +17,7 @@ class StoreTagRequest extends FormRequest */ public function authorize(): bool { - return auth('backend')->check(); + return Gate::check('create', Tag::class); } /** @@ -38,6 +41,11 @@ class StoreTagRequest extends FormRequest { return [ 'name' => 'required|string|min:2|max:25', + 'slug' => [ + 'required', + 'string', + Rule::unique('tags', 'slug')->where('tenant_id', tenant('id')) + ] ]; } @@ -49,8 +57,8 @@ class StoreTagRequest extends FormRequest public function attributes(): array { return [ - 'name' => trans('name of the tag'), - 'slug' => trans('slug of the tag'), + 'name' => trans('models.classes.tag'), + 'slug' => trans('validation.custom.slug'), ]; } } diff --git a/app/Http/Requests/Bo/Tags/UpdateTagRequest.php b/app/Http/Requests/Bo/Tags/UpdateTagRequest.php index 88166e6..c01b3e1 100644 --- a/app/Http/Requests/Bo/Tags/UpdateTagRequest.php +++ b/app/Http/Requests/Bo/Tags/UpdateTagRequest.php @@ -2,6 +2,17 @@ namespace App\Http\Requests\Bo\Tags; +use Illuminate\Support\Facades\Gate; + class UpdateTagRequest extends StoreTagRequest { + /** + * Determine if the user is authorized to make this request. + * + * @return boolean + */ + public function authorize(): bool + { + return Gate::check('update', $this->route('tag')); + } } diff --git a/app/Http/Requests/Bo/Tenants/TenantRequest.php b/app/Http/Requests/Bo/Tenants/StoreTenantRequest.php similarity index 96% rename from app/Http/Requests/Bo/Tenants/TenantRequest.php rename to app/Http/Requests/Bo/Tenants/StoreTenantRequest.php index aeb7239..a4e347f 100644 --- a/app/Http/Requests/Bo/Tenants/TenantRequest.php +++ b/app/Http/Requests/Bo/Tenants/StoreTenantRequest.php @@ -7,7 +7,7 @@ use Illuminate\Foundation\Http\FormRequest; use Illuminate\Support\Facades\Gate; use Illuminate\Support\Str; -class TenantRequest extends FormRequest +class StoreTenantRequest extends FormRequest { /** * Determine if the user is authorized to make this request. diff --git a/app/Http/Requests/Bo/Tenants/UpdateTenantRequest.php b/app/Http/Requests/Bo/Tenants/UpdateTenantRequest.php new file mode 100644 index 0000000..4881595 --- /dev/null +++ b/app/Http/Requests/Bo/Tenants/UpdateTenantRequest.php @@ -0,0 +1,18 @@ +route('tenant')); + } +} diff --git a/app/Http/Requests/Bo/UploadRequest.php b/app/Http/Requests/Bo/UploadRequest.php index 2a0598c..1f9bd1e 100644 --- a/app/Http/Requests/Bo/UploadRequest.php +++ b/app/Http/Requests/Bo/UploadRequest.php @@ -13,7 +13,7 @@ class UploadRequest extends FormRequest */ public function authorize(): bool { - return \auth()->check(); + return \auth('backend')->check(); } /** diff --git a/app/Http/Requests/Bo/Users/StoreUserRequest.php b/app/Http/Requests/Bo/Users/StoreUserRequest.php new file mode 100644 index 0000000..d346768 --- /dev/null +++ b/app/Http/Requests/Bo/Users/StoreUserRequest.php @@ -0,0 +1,61 @@ + 'required|string|min:3|max:255', + 'last_name' => 'required|string|min:3|max:255', + 'email' => [ + 'required', + 'email:rfc,strict,dns,spoof,filter', + Rule::unique('users', 'email') + ], + 'role' => ['required', new Enum(UserRolesEnum::class)], + 'tenants' => 'required|nullable|array|min:1', + 'tenants.*' => 'required|nullable|array|min:1', + 'tenants.*.id' => 'required|nullable|exists:tenants,id', + 'tenants.*.*' => 'required|nullable|string|min:1', + ]; + } + + /** + * Get custom attributes for validator errors. + * + * @return array + */ + public function attributes(): array + { + return [ + 'first_name' => trans('validation.attributes.first_name'), + 'last_name' => trans('validation.attributes.last_name'), + 'email' => trans('validation.attributes.email'), + 'role' => trans('validation.attributes.role'), + ]; + } +} diff --git a/app/Http/Requests/Bo/Users/UpdateUserRequest.php b/app/Http/Requests/Bo/Users/UpdateUserRequest.php new file mode 100644 index 0000000..9471b55 --- /dev/null +++ b/app/Http/Requests/Bo/Users/UpdateUserRequest.php @@ -0,0 +1,42 @@ +route('user')); + } + + /** + * Get the validation rules that apply to the request. + * + * @return array + */ + public function rules(): array + { + /** @var \App\Models\User $userModel */ + $userModel = request()->route()->parameter('user'); + $rules = [ + 'email' => [ + 'required', + 'string', + 'unique:users,email,' . $userModel->getKey(), + 'email:rfc,strict,dns,spoof,filter', + 'max:255' + ], + 'role' => ['sometimes', new Enum(UserRolesEnum::class)], + ]; + return \array_merge(parent::rules(), $rules); + } +} diff --git a/app/Http/Resources/DocumentResource.php b/app/Http/Resources/DocumentResource.php index b7f005d..a7ef613 100644 --- a/app/Http/Resources/DocumentResource.php +++ b/app/Http/Resources/DocumentResource.php @@ -25,7 +25,7 @@ class DocumentResource extends JsonResource 'id' => $document->id, 'uuid' => $document->uuid, 'title' => $document->title, - 'type' => $document->type->value, + 'type' => $document->type, 'directory' => $document->directory_id, 'path' => $document->path, 'lang' => $document->lang->value, diff --git a/app/Lib/Helpers/ToolboxHelper.php b/app/Lib/Helpers/ToolboxHelper.php index be8d16e..f3de0f5 100644 --- a/app/Lib/Helpers/ToolboxHelper.php +++ b/app/Lib/Helpers/ToolboxHelper.php @@ -279,7 +279,7 @@ class ToolboxHelper * @param string $table * @param string $field * @param string|integer|float|boolean|null|array $value - * @param integer|null $id + * @param integer|string|null $id * @param callable|null $query * @return void * @throws \Illuminate\Validation\ValidationException If the field is not unique. @@ -287,8 +287,8 @@ class ToolboxHelper public static function assertFieldIsUnique( string $table, string $field, - string|int|float|bool|null|array $value, - ?int $id = null, + string|int|float|bool|array|null $value, + int|string|null $id = null, ?callable $query = null ) { if (!$query) { diff --git a/app/Models/AppUser.php b/app/Models/AppUser.php index 8f4ff90..8025e8b 100644 --- a/app/Models/AppUser.php +++ b/app/Models/AppUser.php @@ -15,9 +15,9 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant; * @property string $firstname App user first name. * @property string $email App user email. * @property string $password App user password. - * @property integer $tenant_id Tenant key associated. - * @property \App\Enums\AppUserRolesEnum $role App user role. + * @property string|null $tenant_id Associated tenant. * @property boolean $status App user status. + * @property \App\Enums\AppUserRolesEnum $role App user role. * @property integer $order App user order. * @property-read \Carbon\Carbon $created_at Created date. * @property-read \Carbon\Carbon $updated_at Updated date. @@ -116,7 +116,9 @@ class AppUser extends Model */ private static function setOrder(self $model): void { - $model->order = \intval(self::query()->max('order')) + 1; + $model->order = \intval( + self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order') + ) + 1; } // * RELATIONSHIPS diff --git a/app/Models/Directory.php b/app/Models/Directory.php index d79e078..25d089b 100644 --- a/app/Models/Directory.php +++ b/app/Models/Directory.php @@ -10,6 +10,7 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant; /** * @property integer $id Directory id. * @property array $name Directory name. + * @property string|null $tenant_id Associated tenant. * @property integer|null $directory_id Associated directory. * @property integer $order Directory order. * @property boolean $needAuthorization Directory authorization. @@ -73,7 +74,9 @@ class Directory extends Model */ private static function setOrder(self $model): void { - $model->order = \intval(self::query()->max('order')) + 1; + $model->order = \intval( + self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order') + ) + 1; } /** diff --git a/app/Models/Document.php b/app/Models/Document.php index 4ae0ac8..92e1f26 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -3,7 +3,6 @@ namespace App\Models; use App\Enums\Documents\LangEnum; -use App\Enums\Documents\TypeEnum; use App\Lib\Helpers\ToolboxHelper; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -14,9 +13,10 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant; * @property integer $id Document id. * @property integer $uuid Document uuid. * @property string $title Document title. - * @property \App\Enums\Documents\TypeEnum $type Document type. + * @property string $type Document type. + * @property string|null $tenant_id Associated tenant. * @property integer $directory_id Associated directory. - * @property integer $document_id Associated document. + * @property integer|null $document_id Associated document. * @property string $path Document path. * @property \App\Enums\Documents\LangEnum $lang Document lang. * @property boolean $status Document status. @@ -54,7 +54,6 @@ class Document extends Model protected $casts = [ 'status' => 'bool', 'lang' => LangEnum::class, - 'type' => TypeEnum::class, ]; /** @@ -103,7 +102,9 @@ class Document extends Model */ private static function setOrder(self $model): void { - $model->order = \intval(self::query()->max('order')) + 1; + $model->order = \intval( + self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order') + ) + 1; } /** @@ -114,7 +115,7 @@ class Document extends Model */ public static function removeDocument(self $model): void { - $path = \ltrim(\urldecode("storage/docs/{$model->uuid}.{$model->type->label()}"), '/'); + $path = \ltrim(\urldecode("storage/docs/{$model->uuid}.{$model->type}"), '/'); if (\strpos($path, 'storage') !== 0) { return; } diff --git a/app/Models/Formulary.php b/app/Models/Formulary.php index 0ae9fc9..e7b2f0e 100644 --- a/app/Models/Formulary.php +++ b/app/Models/Formulary.php @@ -10,18 +10,19 @@ use Illuminate\Support\Str; use Stancl\Tenancy\Database\Concerns\BelongsToTenant; /** - * @property integer $id Formulary id. - * @property \App\Enums\Formularies\InputTypeEnum $type Formulary type. - * @property string $name Formulary name. - * @property string $slug Formulary slug. - * @property array $placeholder Formulary placeholders. - * @property array> $details Formuary details. - * @property boolean $is_visible Formulary visibility. - * @property boolean $required Formulary required status. - * @property \App\Enums\Formularies\InputStatusEnum $status Formulary status. - * @property integer $order Formulary order. - * @property-read \Carbon\Carbon $created_at Created date. - * @property-read \Carbon\Carbon $updated_at Updated date. + * @property integer $id Formulary id. + * @property \App\Enums\Formularies\InputTypeEnum $type Formulary type. + * @property string $name Formulary name. + * @property string $slug Formulary slug. + * @property array $placeholder Formulary placeholders. + * @property array>|null $details Formuary details. + * @property string|null $tenant_id Associated tenant. + * @property boolean $is_visible Formulary visibility. + * @property boolean $required Formulary required status. + * @property \App\Enums\Formularies\InputStatusEnum $status Formulary status. + * @property integer $order Formulary order. + * @property-read \Carbon\Carbon $created_at Created date. + * @property-read \Carbon\Carbon $updated_at Updated date. */ class Formulary extends Model { @@ -125,6 +126,71 @@ class Formulary extends Model */ private static function setOrder(self $model): void { - $model->order = \intval(self::query()->max('order')) + 1; + $model->order = \intval( + self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order') + ) + 1; + } + + /** + * Set default settings for a specific tenant. + * + * @param \App\Models\Tenant $tenant + * @return void + */ + public function setDefaultFormularies(Tenant $tenant): void + { + // @phpcs:disable Generic.Files.LineLength.TooLong + $array = [ + [ + 'name' => 'e-mail', + 'placeholder' => ['fr' => 'E-mail'], + 'details' => null, + 'type' => InputTypeEnum::email, + 'status' => InputStatusEnum::system, + 'is_visible' => true, + 'required' => true, + ], + [ + 'name' => 'firstname', + 'placeholder' => ['fr' => 'Prénom'], + 'details' => null, + 'type' => InputTypeEnum::text, + 'status' => InputStatusEnum::locked, + 'is_visible' => true, + 'required' => true, + ], + [ + 'name' => 'lastname', + 'placeholder' => ['fr' => 'Nom'], + 'details' => null, + 'type' => InputTypeEnum::text, + 'status' => InputStatusEnum::locked, + 'is_visible' => true, + 'required' => true, + ], + [ + 'name' => 'phone', + 'placeholder' => ['fr' => 'Téléphone'], + 'details' => null, + 'type' => InputTypeEnum::phone, + 'status' => InputStatusEnum::locked, + 'is_visible' => true, + 'required' => true, + ], + ]; + // @phpcs:enable + collect($array)->map(function (array $item) use ($tenant) { + $setting = new self(); + $setting->name = $item['name']; + $setting->slug = Str::of($item['name'])->slug(); + $setting->placeholder = $item['placeholder']; + $setting->details = $item['details']; + $setting->type = $item['type']; + $setting->status = $item['status']; + $setting->is_visible = $item['is_visible']; + $setting->required = $item['required']; + $setting->tenant_id = $tenant->getKey(); + $setting->saveOrFail(); + }); } } diff --git a/app/Models/Setting.php b/app/Models/Setting.php index 73bd762..9f824f3 100644 --- a/app/Models/Setting.php +++ b/app/Models/Setting.php @@ -12,6 +12,7 @@ use Stancl\Tenancy\Database\Concerns\BelongsToTenant; * @property integer $id Settings id. * @property string $key Settings key. * @property string $value Settings value. + * @property string|null $tenant_id Associated tenant. * @property \App\Enums\SettingTypesEnum $type Settings type. * @property-read \Carbon\Carbon $created_at Created date. * @property-read \Carbon\Carbon $updated_at Updated date. @@ -52,6 +53,16 @@ class Setting extends Model */ protected static function booted(): void { + static::creating(function (self $model) { + if ( + $model->key === "SquaredLogo" || + $model->key === "RectangleLogo" || + $model->key === "BackgroundImage" || + $model->key === "HomeButtonImage" + ) { + $model->value = FileStorageHelper::storeFile($model, $model->value, true); + } + }); static::updating(function (self $model) { if ( $model->key === "SquaredLogo" || @@ -73,4 +84,42 @@ class Setting extends Model } }); } + + // * METHODS + + /** + * Set default settings for a specific tenant. + * + * @param \App\Models\Tenant $tenant + * @return void + */ + public function setDefaultSettings(Tenant $tenant): void + { + // @phpcs:disable Generic.Files.LineLength.TooLong + $array = [ + ['key' => 'version', 'value' => '1', 'type' => SettingTypesEnum::numeric], + ['key' => 'ShowIsActive', 'value' => '0', 'type' => SettingTypesEnum::boolean], + ['key' => 'MainColor', 'value' => '#FFF', 'type' => SettingTypesEnum::color], + ['key' => 'SquaredLogo', 'value' => new \SplFileInfo(\resource_path('assets/settings/SquaredLogo.png')), 'type' => SettingTypesEnum::picture], + ['key' => 'RectangleLogo', 'value' => new \SplFileInfo(\resource_path('assets/settings/RectangleLogo.png')), 'type' => SettingTypesEnum::picture], + ['key' => 'BackgroundImage', 'value' => new \SplFileInfo(\resource_path('assets/settings/BackgroundImage.png')), 'type' => SettingTypesEnum::picture], + ['key' => 'HomeButtonImage', 'value' => new \SplFileInfo(\resource_path('assets/settings/HomeButtonImage.png')), 'type' => SettingTypesEnum::picture], + ['key' => 'Button1', 'value' => null, 'type' => SettingTypesEnum::directory], + ['key' => 'Button2', 'value' => null, 'type' => SettingTypesEnum::directory], + ['key' => 'Button3', 'value' => null, 'type' => SettingTypesEnum::directory], + ['key' => 'AppStoreUUID', 'value' => 'c4ac09fd-3992-47bc-bdee-0b7f5505e873', 'type' => SettingTypesEnum::string], + ['key' => 'FormSelect1', 'value' => '{"title":{"fr":"Travaille avec","en":"Work with","de":"Arbeit mit"},"values":{"pole-sable":{"fr":"P\u00f4le Sabl\u00e9","en":"P\u00f4le Sabl\u00e9","de":"P\u00f4le Sabl\u00e9"},"snv":{"fr":"SNV","en":"SNV","de":"SNV"},"pole-sud-est":{"fr":"P\u00f4le Sud-Est","en":"P\u00f4le Sud-Est","de":"P\u00f4le Sud-Est"},"aquitaine":{"fr":"Aquitaine","en":"Aquitaine","de":"Aquitaine"},"sbv":{"fr":"SBV","en":"SBV","de":"SBV"},"arrive":{"fr":"Arriv\u00e9","en":"Arriv\u00e9","de":"Arriv\u00e9"},"plusieurs-filiales":{"fr":"Plusieurs filiales","en":"Several subsidiaries","de":"Mehrere Filialen"},"autre":{"fr":"Autre","en":"Other","de":"Andere"}}}', 'type' => SettingTypesEnum::json], + ['key' => 'FormSelect2', 'value' => '{"title":{"fr":"Type de commerce","en":"Type of business","de":"Art des Gesch\u00e4fts"},"values":{"commerce-de-detail":{"fr":"Commerce de d\u00e9tail","en":"Retail trade","de":"Einzelhandel"},"restauration-commerciale":{"fr":"Restauration Commerciale","en":"Commercial catering","de":"Kommerzielle Restauration"},"restauration-collective":{"fr":"Restauration Collective","en":"Collective catering","de":"Kollektiv Restauration"},"grossiste-distributeur-cash":{"fr":"Grossiste Distributeur Cash","en":"Wholesale Distributor Cash","de":"Gro\u00dfh\u00e4ndler H\u00e4ndler Cash"},"pai":{"fr":"PAI","en":"PAI","de":"PAI"},"home-freezer-center":{"fr":"Home Freezer Center","en":"Home Freezer Center","de":"Home Gefrierzentrum"},"autres":{"fr":"Autre","en":"Other","de":"Andere"}}}', 'type' => SettingTypesEnum::json], + ['key' => 'FormCheck', 'value' => '[{"fr":"Frais","en":"Fresh","de":"Frisch"},{"fr":"Surgel\u00e9s","en":"Frozen food","de":"Tiefk\u00fchlkost"},{"fr":"Volailles crues","en":"Raw poultry","de":"Rohes Gefl\u00fcgel"},{"fr":"\u00c9labor\u00e9s crus","en":"Prepared raw","de":"Roh ausgearbeitet"},{"fr":"\u00c9labor\u00e9s cuits","en":"Cooked products","de":"Gekochte Elaborate"},{"fr":"Bio et Label Rouge","en":"Bio and Label Rouge","de":"Bio und Label Rouge"},{"fr":"BBC","en":"BBC","de":"BBC"},{"fr":"Halal","en":"Halal","de":"Halal"},{"fr":"Autre","en":"Other","de":"Andere"}]', 'type' => SettingTypesEnum::json], + ]; + // @phpcs:enable + collect($array)->map(function (array $item) use ($tenant) { + $setting = new self(); + $setting->key = $item['key']; + $setting->value = $item['value']; + $setting->type = $item['type']; + $setting->tenant_id = $tenant->getKey(); + $setting->saveOrFail(); + }); + } } diff --git a/app/Models/Tag.php b/app/Models/Tag.php index 3a8d236..ab5573c 100644 --- a/app/Models/Tag.php +++ b/app/Models/Tag.php @@ -2,16 +2,15 @@ namespace App\Models; -use App\Lib\Helpers\ToolboxHelper; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -use Illuminate\Support\Str; use Stancl\Tenancy\Database\Concerns\BelongsToTenant; /** * @property integer $id Tag id. * @property string $slug Tag slug. * @property string $name Tag name. + * @property string|null $tenant_id Associated tenant. * @property integer $order Tag order. * @property-read \Carbon\Carbon $created_at Created date. * @property-read \Carbon\Carbon $updated_at Updated date. @@ -39,13 +38,8 @@ class Tag extends Model protected static function booted(): void { static::creating(function (self $tag) { - self::setSlug($tag); - self::assertFieldIsUnique($tag->slug); self::setOrder($tag); }); - static::updating(function (self $tag) { - self::assertFieldIsUnique($tag->slug, $tag->id); - }); static::deleting(function (self $tag) { $tag->documents()->detach(); }); @@ -53,32 +47,6 @@ class Tag extends Model // * METHODS - /** - * Set the slug. - * - * @param \App\Models\Tag $tag - * - * @return void - */ - private static function setSlug(Tag $tag): void - { - $tag->slug = Str::slug($tag->name); - } - - /** - * Asserts using validation that the field is unique. - * - * @param string $slug - * @param integer|null $id - * @return void - * @throws \Illuminate\Validation\ValidationException If field already exists. - */ - private static function assertFieldIsUnique(string $slug, ?int $id = null): void - { - $table = (new self())->getTable(); - ToolboxHelper::assertFieldIsUnique($table, 'slug', $slug, $id); - } - /** * Set order after the last element of the list. * @@ -87,7 +55,9 @@ class Tag extends Model */ private static function setOrder(Tag $tag): void { - $tag->order = \intval(self::query()->max('order')) + 1; + $tag->order = \intval( + self::query()->where('tenant_id', \tenant() ?? Tenant::first()->getKey())->max('order') + ) + 1; } // * RELATIONSHIPS diff --git a/app/Models/Tenant.php b/app/Models/Tenant.php index 4416788..f6e0c45 100644 --- a/app/Models/Tenant.php +++ b/app/Models/Tenant.php @@ -40,17 +40,15 @@ class Tenant extends BaseTenant implements TenantContract protected static function booted(): void { static::creating(function (self $model) { - // * ASSERTIONS self::assertNameIsUnique($model->name); - - // * TRANSFORMATIONS $model->id = Str::slug($model->name); }); + static::created(function (self $model) { + (new Setting())->setDefaultSettings($model); + (new Formulary())->setDefaultFormularies($model); + }); static::updating(function (self $model) { - // * ASSERTIONS - self::assertNameIsUnique($model->name, $model->id); - - // * TRANSFORMATIONS + self::assertNameIsUnique($model->name, $model->getKey()); $model->id = Str::slug($model->name); }); } @@ -60,15 +58,67 @@ class Tenant extends BaseTenant implements TenantContract /** * Asserts using validation that the name is unique * - * @param string|null $name - * @param integer|null $id + * @param string|null $name + * @param string|null $id * @return void * @throws \Illuminate\Validation\ValidationException If name already exists. */ - private static function assertNameIsUnique(string|null $name, ?int $id = null): void + private static function assertNameIsUnique(string|null $name, ?string $id = null): void { $table = (new self())->getTable(); ToolboxHelper::assertFieldIsUnique($table, 'name', $name, $id); ToolboxHelper::assertFieldIsUnique($table, 'id', Str::slug($name), $id); } + + // * RELATIONSHIPS + + /** + * Has Many Relationship Method. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function appUsers(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(AppUser::class); + } + + /** + * Has Many Relationship Method. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function directories(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Directory::class); + } + + /** + * Has Many Relationship Method. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function documents(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Document::class); + } + + /** + * Has Many Relationship Method. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function formularies(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Formulary::class); + } + + /** + * Has Many Relationship Method. + * + * @return \Illuminate\Database\Eloquent\Relations\HasMany + */ + public function settings(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(Setting::class); + } } diff --git a/app/Models/User.php b/app/Models/User.php index 5c364bc..77cb7be 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -5,25 +5,19 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -use Illuminate\Support\Facades\Hash; use App\Enums\UserRolesEnum; -use Stancl\Tenancy\Database\Concerns\BelongsToTenant; /** - * @property integer $firstname User first name. - * @property string $lastname User last name. + * @property integer $first_name User first name. + * @property string $last_name User last name. * @property string $email User email. - * @property string $email_verified_at Email verified date. - * @property string $password User password. - * @property string $remember_token User token remember. - * @property integer $tenant_id Tenant key associated. * @property \App\Enums\UserRolesEnum $role User role. + * @property \Carbon\Carbon $last_connection Last connection date. * @property-read \Carbon\Carbon $created_at Created date. * @property-read \Carbon\Carbon $updated_at Updated date. */ class User extends Authenticatable { - use BelongsToTenant; use HasFactory; use Notifiable; @@ -33,65 +27,23 @@ class User extends Authenticatable * @var list */ protected $fillable = [ - 'firstname', - 'lastname', + 'first_name', + 'last_name', 'email', - 'email_verified_at', - 'password', + 'last_connection', 'role', ]; - /** - * The attributes that should be hidden for serialization. - * - * @var list - */ - protected $hidden = [ - 'password', - 'remember_token', - ]; - /** * The attributes that should be cast. * * @var array */ protected $casts = [ - 'email_verified_at' => 'datetime', - 'role' => UserRolesEnum::class + 'last_connection' => 'datetime', + 'role' => UserRolesEnum::class ]; - /** - * Perform any actions required after the model boots. - * - * @return void - */ - protected static function booted(): void - { - // * EVENT CLOSURES. - static::creating(function (self $model) { - self::updatePassword($model); - }); - static::updating(function (self $model) { - self::updatePassword($model); - }); - } - - // * METHODS - - /** - * Update password. - * - * @param self $model - * @return void - */ - private static function updatePassword(self $model): void - { - if (Hash::needsRehash($model->password)) { - $model->password = Hash::make($model->password); - } - } - // * RELATIONSHIPS /** @@ -103,4 +55,14 @@ class User extends Authenticatable { return $this->morphToMany(Directory::class, 'directorables'); } + + /** + * The tenants that belong to the user. + * + * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany + */ + public function tenants(): \Illuminate\Database\Eloquent\Relations\BelongsToMany + { + return $this->belongsToMany(Tenant::class); + } } diff --git a/app/Policies/AppUserPolicy.php b/app/Policies/AppUserPolicy.php index 49e4d24..1c4f2b1 100644 --- a/app/Policies/AppUserPolicy.php +++ b/app/Policies/AppUserPolicy.php @@ -2,20 +2,85 @@ namespace App\Policies; -use App\Enums\UserRolesEnum; use App\Models\User; use App\Policies\Rules\UserStaticRules; class AppUserPolicy { /** - * Determine whether the user can publish/unpublish the model. + * Determine whether the user can view any models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function viewAny(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can view the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function view(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function update(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function delete(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can change order of the model. * * @param \App\Models\User $authUser * @return boolean */ public function changeOrder(User $authUser): bool { - return UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can duplicate the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function duplicate(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); } } diff --git a/app/Policies/ContactPolicy.php b/app/Policies/ContactPolicy.php new file mode 100644 index 0000000..7d55ee0 --- /dev/null +++ b/app/Policies/ContactPolicy.php @@ -0,0 +1,75 @@ +is_system && UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Directory $directory + * @return boolean + */ + public function delete(User $authUser, Directory $directory): bool + { + return !$directory->is_system && UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can change order of the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function changeOrder(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } +} diff --git a/app/Policies/DocumentPolicy.php b/app/Policies/DocumentPolicy.php index 3502407..85c702b 100644 --- a/app/Policies/DocumentPolicy.php +++ b/app/Policies/DocumentPolicy.php @@ -2,20 +2,74 @@ namespace App\Policies; -use App\Enums\UserRolesEnum; use App\Models\User; use App\Policies\Rules\UserStaticRules; class DocumentPolicy { /** - * Determine whether the user can publish/unpublish the model. + * Determine whether the user can view any models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function viewAny(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can view the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function view(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function update(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function delete(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can change order of the model. * * @param \App\Models\User $authUser * @return boolean */ public function changeOrder(User $authUser): bool { - return UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + return UserStaticRules::restrictTenant($authUser); } } diff --git a/app/Policies/FormularyPolicy.php b/app/Policies/FormularyPolicy.php index 27f0c62..f8958cd 100644 --- a/app/Policies/FormularyPolicy.php +++ b/app/Policies/FormularyPolicy.php @@ -2,20 +2,97 @@ namespace App\Policies; -use App\Enums\UserRolesEnum; +use App\Enums\Formularies\InputStatusEnum; +use App\Models\Formulary; use App\Models\User; use App\Policies\Rules\UserStaticRules; class FormularyPolicy { /** - * Determine whether the user can publish/unpublish the model. + * Determine whether the user can view any models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function viewAny(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can view the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Formulary $formulary + * @return boolean + */ + public function view(User $authUser, Formulary $formulary): bool + { + return !$formulary->status->equals(InputStatusEnum::system) && + UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Formulary $formulary + * @return boolean + */ + public function update(User $authUser, Formulary $formulary): bool + { + return !$formulary->status->equals(InputStatusEnum::system) && + UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Formulary $formulary + * @return boolean + */ + public function delete(User $authUser, Formulary $formulary): bool + { + return !$formulary->status->equals(InputStatusEnum::system) && + !$formulary->status->equals(InputStatusEnum::locked) && + UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can duplicate the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\Formulary $formulary + * @return boolean + */ + public function duplicate(User $authUser, Formulary $formulary): bool + { + return !$formulary->status->equals(InputStatusEnum::system) && + !$formulary->status->equals(InputStatusEnum::locked) && + UserStaticRules::restrictTenant($authUser); + } + + /** + * Determine whether the user can change order of the model. * * @param \App\Models\User $authUser * @return boolean */ public function changeOrder(User $authUser): bool { - return UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + return UserStaticRules::restrictTenant($authUser); } } diff --git a/app/Policies/Rules/UserStaticRules.php b/app/Policies/Rules/UserStaticRules.php index 4242167..ae7abd5 100644 --- a/app/Policies/Rules/UserStaticRules.php +++ b/app/Policies/Rules/UserStaticRules.php @@ -3,6 +3,7 @@ namespace App\Policies\Rules; use App\Enums\UserRolesEnum; +use App\Models\User; use Illuminate\Database\Eloquent\Model; /** @@ -41,7 +42,19 @@ abstract class UserStaticRules */ public static function atLeastRole(Model $authModel, UserRolesEnum $minRole): bool { - // phpcs:enable return isset($authModel->role) && $authModel->role->value() <= $minRole->value(); } + + /** + * Allow client users associated to the tenant OR admin users. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public static function restrictTenant(User $authUser): bool + { + return UserStaticRules::atLeastRole($authUser, UserRolesEnum::client) && + \in_array(\tenant()->getKey(), $authUser->tenants->pluck('id')->toArray()) || + UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } } diff --git a/app/Policies/SettingPolicy.php b/app/Policies/SettingPolicy.php new file mode 100644 index 0000000..bbb704a --- /dev/null +++ b/app/Policies/SettingPolicy.php @@ -0,0 +1,42 @@ +getRouteKey() === $user->getRouteKey() || + UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } + + /** + * Determine whether the user can create models. + * + * @param \App\Models\User $authUser + * @return boolean + */ + public function create(User $authUser): bool + { + return UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } + + /** + * Determine whether the user can update the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function update(User $authUser, User $user): bool + { + return ( + UserStaticRules::atLeastRole($authUser, UserRolesEnum::client) && + $authUser->getRouteKey() === $user->getRouteKey() + ) || UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } + + /** + * Determine whether the user can delete the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function delete(User $authUser, User $user): bool + { + return $authUser->getRouteKey() !== $user->getRouteKey() && + UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } + + /** + * Determine whether the user can duplicate the model. + * + * @param \App\Models\User $authUser + * @param \App\Models\User $user + * @return boolean + */ + public function duplicate(User $authUser, User $user): bool + { + return ( + UserStaticRules::atLeastRole($authUser, UserRolesEnum::client) && + $authUser->getRouteKey() === $user->getRouteKey() + ) || UserStaticRules::atLeastRole($authUser, UserRolesEnum::admin); + } +} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index 20734bc..698232c 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -3,7 +3,6 @@ namespace App\Providers; use App\Enums\UserRolesEnum; -use App\Models\Directory; use App\Models\User; use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; use Illuminate\Support\Facades\Gate; @@ -19,14 +18,6 @@ class AuthServiceProvider extends ServiceProvider { $this->registerPolicies(); - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed - Gate::define('edit', function (User $user, Directory $directory) { - return !$directory->is_system; - }); - // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.FoundBeforeLastUsed - Gate::define('delete', function (User $user, Directory $directory) { - return !$directory->is_system; - }); Gate::define('isAdmin', function (User $user) { return $user->role->equals(UserRolesEnum::admin); }); diff --git a/app/Providers/TenancyServiceProvider.php b/app/Providers/TenancyServiceProvider.php index d94703c..df91bfc 100644 --- a/app/Providers/TenancyServiceProvider.php +++ b/app/Providers/TenancyServiceProvider.php @@ -15,26 +15,32 @@ use Stancl\Tenancy\Middleware; class TenancyServiceProvider extends ServiceProvider { - // By default, no namespace is used to support the callable array syntax. + /** + * By default, no namespace is used to support the callable array syntax. + * + * @var string + */ public static string $controllerNamespace = ''; - public function events() + /** @return array */ + public function events(): array { return [ - // Tenant events + // Tenant events. Events\CreatingTenant::class => [], Events\TenantCreated::class => [ JobPipeline::make([ Jobs\CreateDatabase::class, Jobs\MigrateDatabase::class, // Jobs\SeedDatabase::class, - // Your own jobs to prepare the tenant. - // Provision API keys, create S3 buckets, anything you want! - - ])->send(function (Events\TenantCreated $event) { - return $event->tenant; - })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production. + // Provision API keys, create S3 buckets, anything you want ! + ]) + ->send(function (Events\TenantCreated $event) { + return $event->tenant; + }) + // `false` by default, but you probably want to make this `true` for production. + ->shouldBeQueued(false), ], Events\SavingTenant::class => [], Events\TenantSaved::class => [], @@ -44,12 +50,15 @@ class TenancyServiceProvider extends ServiceProvider Events\TenantDeleted::class => [ JobPipeline::make([ Jobs\DeleteDatabase::class, - ])->send(function (Events\TenantDeleted $event) { - return $event->tenant; - })->shouldBeQueued(false), // `false` by default, but you probably want to make this `true` for production. + ]) + ->send(function (Events\TenantDeleted $event) { + return $event->tenant; + }) + // `false` by default, but you probably want to make this `true` for production. + ->shouldBeQueued(false), ], - // Domain events + // Domain events. Events\CreatingDomain::class => [], Events\DomainCreated::class => [], Events\SavingDomain::class => [], @@ -59,14 +68,14 @@ class TenancyServiceProvider extends ServiceProvider Events\DeletingDomain::class => [], Events\DomainDeleted::class => [], - // Database events + // Database events. Events\DatabaseCreated::class => [], Events\DatabaseMigrated::class => [], Events\DatabaseSeeded::class => [], Events\DatabaseRolledBack::class => [], Events\DatabaseDeleted::class => [], - // Tenancy events + // Tenancy events. Events\InitializingTenancy::class => [], Events\TenancyInitialized::class => [ Listeners\BootstrapTenancy::class, @@ -82,22 +91,25 @@ class TenancyServiceProvider extends ServiceProvider Events\RevertingToCentralContext::class => [], Events\RevertedToCentralContext::class => [], - // Resource syncing + // Resource syncing. Events\SyncedResourceSaved::class => [ Listeners\UpdateSyncedResource::class, ], - // Fired only when a synced resource is changed in a different DB than the origin DB (to avoid infinite loops) + // Fired only when a synced resource is changed in a different DB + // than the origin DB (to avoid infinite loops). Events\SyncedResourceChangedInForeignDatabase::class => [], ]; } - public function register() + /** @return void */ + public function register(): void { - // + // Silence is golden. } - public function boot() + /** @return void */ + public function boot(): void { $this->bootEvents(); $this->mapRoutes(); @@ -105,7 +117,8 @@ class TenancyServiceProvider extends ServiceProvider $this->makeTenancyMiddlewareHighestPriority(); } - protected function bootEvents() + /** @return void */ + protected function bootEvents(): void { foreach ($this->events() as $event => $listeners) { foreach ($listeners as $listener) { @@ -118,7 +131,8 @@ class TenancyServiceProvider extends ServiceProvider } } - protected function mapRoutes() + /** @return void */ + protected function mapRoutes(): void { $this->app->booted(function () { if (file_exists(base_path('routes/tenant.php'))) { @@ -128,10 +142,11 @@ class TenancyServiceProvider extends ServiceProvider }); } - protected function makeTenancyMiddlewareHighestPriority() + /** @return void */ + protected function makeTenancyMiddlewareHighestPriority(): void { $tenancyMiddleware = [ - // Even higher priority than the initialization middleware + // Even higher priority than the initialization middleware. Middleware\PreventAccessFromCentralDomains::class, Middleware\InitializeTenancyByDomain::class, diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index 0326246..82bc656 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -4,8 +4,9 @@ namespace App\Providers; use App\Models\Tenant; use Illuminate\Pagination\Paginator; -use Illuminate\Support\ServiceProvider; +use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\View; +use Illuminate\Support\ServiceProvider; class ViewServiceProvider extends ServiceProvider { @@ -25,7 +26,8 @@ class ViewServiceProvider extends ServiceProvider Paginator::useBootstrap(); } } - - View::share('tenantModels', Tenant::get()); + if (Schema::hasTable('tenants')) { + View::share('tenantsGlobal', Tenant::get()); + } } } diff --git a/app/Traits/Controllers/ChangesModelOrder.php b/app/Traits/Controllers/ChangesModelOrder.php index bc8783a..0bedba8 100644 --- a/app/Traits/Controllers/ChangesModelOrder.php +++ b/app/Traits/Controllers/ChangesModelOrder.php @@ -23,7 +23,7 @@ trait ChangesModelOrder $paramNames = collect(request()->route()->parameterNames); $className = Str::of($paramNames->get($paramNames->count() - 2))->lower()->studly(); $className = "\\App\\Models\\{$className}"; - $model = Arr::get($params, $paramNames->count() - 2); + $model = Arr::get($params, $paramNames->count() - 3); if (!class_exists($className)) { throw new \RuntimeException("The {$className} class doesn't exist."); @@ -39,7 +39,7 @@ trait ChangesModelOrder /** @var \Illuminate\Database\Eloquent\Model|null */ $model = $className::query() - ->where('tenant_id', \tenant()) + ->where('tenant_id', \tenant()->getKey()) ->where($model->getRouteKeyName(), $model->getRouteKey()) ->first(); @@ -50,7 +50,7 @@ trait ChangesModelOrder return DB::transaction(function () use ($request, $className, $model) { /** @var \Illuminate\Database\Eloquent\Model|null */ $tmp = $className::query() - ->where('tenant_id', \tenant()) + ->where('tenant_id', \tenant()->getKey()) ->where('order', $request->direction ? '<' : '>', $model->getOriginal('order')) ->orderBy('order', $request->direction ? 'DESC' : 'ASC')->first(); diff --git a/config/breadcrumbs.php b/config/breadcrumbs.php new file mode 100644 index 0000000..290a566 --- /dev/null +++ b/config/breadcrumbs.php @@ -0,0 +1,77 @@ + 'modules/breadcrumbs/breadcrumb-layout', + + /* + |-------------------------------------------------------------------------- + | Breadcrumbs File(s) + |-------------------------------------------------------------------------- + | + | The file(s) where breadcrumbs are defined. e.g. + | + | - base_path('routes/breadcrumbs.php') + | - glob(base_path('breadcrumbs/*.php')) + | + */ + + 'files' => [ + base_path('routes/breadcrumbs-bo.php'), + ], + + /* + |-------------------------------------------------------------------------- + | Exceptions + |-------------------------------------------------------------------------- + | + | Determine when to throw an exception. + | + */ + + // * When route-bound breadcrumbs are used but the current route doesn't have a name (UnnamedRouteException) + 'unnamed-route-exception' => true, + + // * When route-bound breadcrumbs are used and the matching breadcrumb doesn't exist (InvalidBreadcrumbException) + 'missing-route-bound-breadcrumb-exception' => true, + + // * When a named breadcrumb is used but doesn't exist (InvalidBreadcrumbException) + 'invalid-named-breadcrumb-exception' => true, + + /* + |-------------------------------------------------------------------------- + | Classes + |-------------------------------------------------------------------------- + | + | Subclass the default classes for more advanced customisations. + | + */ + + // * Manager + 'manager-class' => Diglactic\Breadcrumbs\Manager::class, + + // * Generator + 'generator-class' => Diglactic\Breadcrumbs\Generator::class, + +]; diff --git a/config/purifier.php b/config/purifier.php index 35a8914..e38299c 100644 --- a/config/purifier.php +++ b/config/purifier.php @@ -29,7 +29,6 @@ return [ // * For iframes 'URI.SafeIframeRegexp' => "%^(http://|https://|//)(www.youtube.com/embed/|player.vimeo.com/video/|vimeo.com/)%", 'HTML.Doctype' => 'HTML 4.01 Transitional', - // TODO: Rules en FULL CKEDITOR. // * Legacy filter en full avec safe legacy. // 'HTML.Allowed' => 'h1[style],h2[style],h3[style],h4[style],h5[style],h6[style],figure[class|style],oembed[url],div[style],b,strong,i,em,u,a[href|title|target|rel],ul[style],ol[style],dl[style],li,p[style],br,span[style|class],img[width|height|alt|src|style|class|title],iframe[src|width|height|class|frameborder|style],table[style|align|border|cellspacing|cellpadding|width],thead,tbody,th[colspan|align|valign|style],tr[align|valign|style],td[style|colspan|rowspan|scope|align|valign],hr,dl,dt', // * Restricted basics. diff --git a/config/tenancy.php b/config/tenancy.php index 45499dc..e3cc1c8 100644 --- a/config/tenancy.php +++ b/config/tenancy.php @@ -1,5 +1,7 @@ Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, // Separate by schema instead of database + // Separate by schema instead of database + // 'pgsql' => Stancl\Tenancy\TenantDatabaseManagers\PostgreSQLSchemaManager::class, ], ], @@ -86,7 +89,8 @@ return [ * You can clear cache selectively by specifying the tag. */ 'cache' => [ - 'tag_base' => 'tenant', // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call. + // This tag_base, followed by the tenant_id, will form a tag that will be applied on each cache call. + 'tag_base' => 'tenant', ], /** @@ -147,8 +151,10 @@ return [ * either using the Redis facade or by injecting it as a dependency. */ 'redis' => [ - 'prefix_base' => 'tenant', // Each key in Redis will be prepended by this prefix_base, followed by the tenant id. - 'prefixed_connections' => [ // Redis connections whose keys are prefixed, to separate one tenant's keys from another. + // Each key in Redis will be prepended by this prefix_base, followed by the tenant id. + 'prefix_base' => 'tenant', + // Redis connections whose keys are prefixed, to separate one tenant's keys from another. + 'prefixed_connections' => [ // 'default', ], ], @@ -165,8 +171,10 @@ return [ // Stancl\Tenancy\Features\UserImpersonation::class, // Stancl\Tenancy\Features\TelescopeTags::class, // Stancl\Tenancy\Features\UniversalRoutes::class, - // Stancl\Tenancy\Features\TenantConfig::class, // https://tenancyforlaravel.com/docs/v3/features/tenant-config - // Stancl\Tenancy\Features\CrossDomainRedirect::class, // https://tenancyforlaravel.com/docs/v3/features/cross-domain-redirect + // https://tenancyforlaravel.com/docs/v3/features/tenant-config + // Stancl\Tenancy\Features\TenantConfig::class, + // https://tenancyforlaravel.com/docs/v3/features/cross-domain-redirect + // Stancl\Tenancy\Features\CrossDomainRedirect::class, // Stancl\Tenancy\Features\ViteBundler::class, ], @@ -183,7 +191,8 @@ return [ * Parameters used by the tenants:migrate command. */ 'migration_parameters' => [ - '--force' => true, // This needs to be true to run migrations in production. + // This needs to be true to run migrations in production. + '--force' => true, '--path' => [database_path('migrations/tenant')], '--realpath' => true, ], @@ -192,7 +201,9 @@ return [ * Parameters used by the tenants:seed command. */ 'seeder_parameters' => [ - '--class' => 'DatabaseSeeder', // root seeder class - // '--force' => true, // This needs to be true to seed tenant databases in production + // Root seeder class. + '--class' => 'DatabaseSeeder', + // This needs to be true to seed tenant databases in production. + // '--force' => true, ], ]; diff --git a/resources/assets/settings/BackgroundImage.png b/resources/assets/settings/BackgroundImage.png new file mode 100644 index 0000000..a4eee84 Binary files /dev/null and b/resources/assets/settings/BackgroundImage.png differ diff --git a/resources/assets/settings/HomeButtonImage.png b/resources/assets/settings/HomeButtonImage.png new file mode 100644 index 0000000..0e12966 Binary files /dev/null and b/resources/assets/settings/HomeButtonImage.png differ diff --git a/resources/assets/settings/RectangleLogo.png b/resources/assets/settings/RectangleLogo.png new file mode 100644 index 0000000..b1cdbaa Binary files /dev/null and b/resources/assets/settings/RectangleLogo.png differ diff --git a/resources/assets/settings/SquaredLogo.png b/resources/assets/settings/SquaredLogo.png new file mode 100644 index 0000000..1c99804 Binary files /dev/null and b/resources/assets/settings/SquaredLogo.png differ diff --git a/resources/lang/en/back.php b/resources/lang/en/back.php index cc77845..11e6bfb 100644 --- a/resources/lang/en/back.php +++ b/resources/lang/en/back.php @@ -2,5 +2,5 @@ return [ 'home' => 'Home', - 'admin' => 'Administration', + 'admin' => 'Avanced', ]; diff --git a/resources/lang/en/models.php b/resources/lang/en/models.php index 41ac63a..b00e75a 100644 --- a/resources/lang/en/models.php +++ b/resources/lang/en/models.php @@ -2,14 +2,14 @@ return [ 'classes' => [ - 'app_users' => 'Application\'s users', - 'directories' => 'Directories', - 'documents' => 'Documents', - 'users' => 'Users', - 'settings' => 'Settings', - 'tags' => 'Tags', - 'formularies' => 'Formularies', - 'contacts' => 'Contacts', - 'tenants' => 'Tenants', + 'app_user' => 'App. user', + 'directory' => 'Directory', + 'document' => 'Document', + 'user' => 'User', + 'setting' => 'Setting', + 'tag' => 'Tag', + 'formulary' => 'Formulary', + 'contact' => 'Contact', + 'tenant' => 'Tenant', ] ]; diff --git a/resources/lang/en/validation.php b/resources/lang/en/validation.php index 999ba8c..9f65330 100644 --- a/resources/lang/en/validation.php +++ b/resources/lang/en/validation.php @@ -125,77 +125,93 @@ return [ 'url' => 'The :attribute format is invalid.', 'uuid' => 'The :attribute must be a valid UUID.', 'attributes' => [ - 'address' => 'address', - 'age' => 'age', - 'amount' => 'amount', - 'area' => 'area', - 'available' => 'available', - 'birthday' => 'birthday', - 'body' => 'body', - 'city' => 'city', - 'content' => 'content', - 'country' => 'country', - 'created_at' => 'created at', - 'creator' => 'creator', - 'current_password' => 'current password', - 'date' => 'date', - 'date_of_birth' => 'date of birth', - 'day' => 'day', - 'deleted_at' => 'deleted at', - 'description' => 'description', - 'district' => 'district', - 'duration' => 'duration', - 'email' => 'email address', - 'excerpt' => 'excerpt', - 'filter' => 'filter', - 'first_name' => 'first name', - 'gender' => 'gender', - 'group' => 'group', - 'hour' => 'hour', - 'image' => 'image', - 'last_name' => 'last name', - 'lesson' => 'lesson', - 'line_address_1' => 'address line 1', - 'line_address_2' => 'address line 2', - 'message' => 'message', - 'middle_name' => 'middle name', - 'minute' => 'minute', - 'mobile' => 'mobile', - 'month' => 'month', - 'name' => 'name', - 'national_code' => 'national code', - 'number' => 'number', - 'password' => 'password', - 'password_confirmation' => 'password confirmation', - 'phone' => 'phone', - 'photo' => 'photo', - 'postal_code' => 'postal code', - 'price' => 'price', - 'province' => 'province', + 'address' => 'address', + 'age' => 'age', + 'amount' => 'amount', + 'area' => 'area', + 'available' => 'available', + 'birthday' => 'birthday', + 'body' => 'body', + 'city' => 'city', + 'content' => 'content', + 'country' => 'country', + 'created_at' => 'created at', + 'creator' => 'creator', + 'current_password' => 'current password', + 'date' => 'date', + 'date_of_birth' => 'date of birth', + 'day' => 'day', + 'deleted_at' => 'deleted at', + 'description' => 'description', + 'district' => 'district', + 'duration' => 'duration', + 'email' => 'email address', + 'excerpt' => 'excerpt', + 'filter' => 'filter', + 'first_name' => 'first name', + 'gender' => 'gender', + 'group' => 'group', + 'hour' => 'hour', + 'image' => 'image', + 'last_name' => 'last name', + 'lesson' => 'lesson', + 'line_address_1' => 'address line 1', + 'line_address_2' => 'address line 2', + 'message' => 'message', + 'middle_name' => 'middle name', + 'minute' => 'minute', + 'mobile' => 'mobile', + 'month' => 'month', + 'name' => 'name', + 'national_code' => 'national code', + 'number' => 'number', + 'password' => 'password', + 'password_confirmation' => 'password confirmation', + 'phone' => 'phone', + 'photo' => 'photo', + 'postal_code' => 'postal code', + 'price' => 'price', + 'province' => 'province', 'recaptcha_response_field' => 'recaptcha response field', - 'remember' => 'remember', - 'restored_at' => 'restored at', - 'result_text_under_image' => 'result text under image', - 'role' => 'role', - 'second' => 'second', - 'sex' => 'sex', - 'short_text' => 'short text', - 'size' => 'size', - 'state' => 'state', - 'street' => 'street', - 'student' => 'student', - 'subject' => 'subject', - 'teacher' => 'teacher', - 'terms' => 'terms', - 'test_description' => 'test description', - 'test_locale' => 'test locale', - 'test_name' => 'test name', - 'text' => 'text', - 'time' => 'time', - 'title' => 'title', - 'updated_at' => 'updated at', - 'username' => 'username', - 'year' => 'year', - 'zipcode' => 'zipcode', + 'remember' => 'remember', + 'restored_at' => 'restored at', + 'result_text_under_image' => 'result text under image', + 'role' => 'role', + 'second' => 'second', + 'sex' => 'sex', + 'short_text' => 'short text', + 'size' => 'size', + 'state' => 'state', + 'street' => 'street', + 'student' => 'student', + 'subject' => 'subject', + 'teacher' => 'teacher', + 'terms' => 'terms', + 'test_description' => 'test description', + 'test_locale' => 'test locale', + 'test_name' => 'test name', + 'text' => 'text', + 'time' => 'time', + 'title' => 'title', + 'updated_at' => 'updated at', + 'username' => 'username', + 'year' => 'year', + 'zipcode' => 'zipcode', ], + 'custom' => [ + 'authorization' => 'authorization', + 'detail' => 'detail', + 'key' => 'key', + 'lang' => 'lang', + 'order' => 'order', + 'path' => 'path', + 'placeholder' => 'placeholder', + 'required' => 'required', + 'role' => 'role', + 'slug' => 'slug', + 'status' => 'status', + 'type' => 'type', + 'value' => 'value', + 'visibility' => 'visibility', + ] ]; diff --git a/resources/lang/fr/back.php b/resources/lang/fr/back.php index bdd6efd..94f77bc 100644 --- a/resources/lang/fr/back.php +++ b/resources/lang/fr/back.php @@ -2,5 +2,5 @@ return [ 'home' => 'Accueil', - 'admin' => 'Administration', + 'admin' => 'Avancé', ]; diff --git a/resources/lang/fr/models.php b/resources/lang/fr/models.php index 1cf9b44..89ce3fd 100644 --- a/resources/lang/fr/models.php +++ b/resources/lang/fr/models.php @@ -2,14 +2,14 @@ return [ 'classes' => [ - 'app_users' => 'Application utilisateurs', - 'directories' => 'Dossiers', - 'documents' => 'Documents', - 'users' => 'Utilisateurs', - 'settings' => 'Paramètres', - 'tags' => 'Étiquettes', - 'formularies' => 'Formulaires', - 'contacts' => 'Contacts', - 'tenants' => 'Tenants', + 'app_user' => 'App. utilisateur', + 'directory' => 'Dossier', + 'document' => 'Document', + 'user' => 'Utilisateur', + 'setting' => 'Paramètre', + 'tag' => 'Étiquette', + 'formulary' => 'Formulaire', + 'contact' => 'Contact', + 'tenant' => 'Tenant', ] ]; diff --git a/resources/lang/fr/validation.php b/resources/lang/fr/validation.php index eb74d0c..2503436 100644 --- a/resources/lang/fr/validation.php +++ b/resources/lang/fr/validation.php @@ -199,4 +199,20 @@ return [ 'year' => 'année', 'zipcode' => 'code postal' ], + 'custom' => [ + 'authorization' => 'autorisation', + 'detail' => 'détail', + 'key' => 'clé', + 'lang' => 'langue', + 'order' => 'ordre', + 'path' => 'chemin', + 'placeholder' => 'placeholder', + 'required' => 'requis', + 'role' => 'rôle', + 'slug' => 'slug', + 'status' => 'statut', + 'type' => 'type', + 'value' => 'valeur', + 'visibility' => 'visibilité', + ] ]; diff --git a/resources/sass/bo/base/_common.scss b/resources/sass/bo/base/_common.scss index d1e69ee..b6f3cf4 100644 --- a/resources/sass/bo/base/_common.scss +++ b/resources/sass/bo/base/_common.scss @@ -1,16 +1,23 @@ -.need-word-counter { - position: relative; - border-radius: 0 0 0.375rem 0.375rem; +// * LAYOUT - &-password { - border-radius: 0 0 0 0.375rem !important; +body { + .navbar-brand > .logo { + max-width: 9rem; + } - &-btn { - border-radius: 0 0 0.375rem !important; - } + > main { + min-height: calc(100vh - (#{$bo-header-height} + #{$bo-footer-height})); + margin-top: $bo-header-height; + } + + > footer { + height: $bo-footer-height; + box-shadow: 0 -0.125rem 0.25rem rgb(0 0 0 / 7.5%) !important; } } +// * OTHERS + .table { &.fix-action { th:last-child, @@ -39,25 +46,33 @@ max-height: 5rem; } +.w-fit { + width: fit-content; +} + +.btn-md { + height: 38px; +} + +.btn-sm { + height: 31px; +} + .pagination-custom .dropdown-menu { overflow: auto; min-width: 5rem; max-height: 178px; } -.btn-md { - min-width: 38px; - height: 38px; -} +.need-word-counter { + position: relative; + border-radius: 0 0 0.375rem 0.375rem; -.w-fit { - width: fit-content; -} + &-password { + border-radius: 0 0 0 0.375rem !important; -.table-col-sorter { - height: 3rem; -} - -main { - min-height: calc(100vh - 56px - 40px); + &-btn { + border-radius: 0 0 0.375rem !important; + } + } } diff --git a/resources/sass/bo/utilities/_variables.scss b/resources/sass/bo/utilities/_variables.scss index 81f7339..ffb8557 100644 --- a/resources/sass/bo/utilities/_variables.scss +++ b/resources/sass/bo/utilities/_variables.scss @@ -15,3 +15,7 @@ $font-default-family-base: system-ui, -apple-system, 'Segoe UI', 'Roboto', 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; // - The default site font family. $font-default-size-base: 1rem; // Assumes the browser default, typically `16px` $line-default-height-base: 1.5; + +// * DIMENSIONS +$bo-header-height: 4.8rem; +$bo-footer-height: 2rem; diff --git a/resources/sass/bo/vendor/_bootstrap.scss b/resources/sass/bo/vendor/_bootstrap.scss index 9bcf8b3..9da2e0d 100644 --- a/resources/sass/bo/vendor/_bootstrap.scss +++ b/resources/sass/bo/vendor/_bootstrap.scss @@ -17,6 +17,7 @@ @import "bootstrap/scss/reboot"; @import "bootstrap/scss/accordion"; @import "bootstrap/scss/alert"; +@import "bootstrap/scss/badge"; @import "bootstrap/scss/breadcrumb"; @import "bootstrap/scss/button-group"; @import "bootstrap/scss/buttons"; diff --git a/resources/ts/bo/components/CrudDocumentInputComponent.vue b/resources/ts/bo/components/CrudDocumentInputComponent.vue index 5054514..c9c075a 100644 --- a/resources/ts/bo/components/CrudDocumentInputComponent.vue +++ b/resources/ts/bo/components/CrudDocumentInputComponent.vue @@ -273,10 +273,14 @@ onMounted(() => { intDirectories.value = data.directories; intModel.value = data.model; if (intModel.value != null) { - intDocumentDatas.value.path = String(intModel.value.path); - intDocumentDatas.value.uuid = String(intModel.value.uuid); - intDocumentDatas.value.type = String(intModel.value.type); - intIsDocFR.value = intModel.value.lang === 1 ? true : false; + if (intModel.value.lang === undefined) { + intIsDocFR.value = true; + } else { + intDocumentDatas.value.path = String(intModel.value.path); + intDocumentDatas.value.uuid = String(intModel.value.uuid); + intDocumentDatas.value.type = String(intModel.value.type); + intIsDocFR.value = intModel.value.lang === 1 ? true : false; + } } intCsrf.value = data.csrf; nextTick(() => { diff --git a/resources/ts/bo/components/DirectoriesComponent.vue b/resources/ts/bo/components/DirectoriesComponent.vue index 1cb091c..a125c74 100644 --- a/resources/ts/bo/components/DirectoriesComponent.vue +++ b/resources/ts/bo/components/DirectoriesComponent.vue @@ -37,7 +37,7 @@ }" >
{ - let elements = document.getElementsByClassName("confirmDeleteJS"); + let elements = document.getElementsByClassName("confirmDeleteTS"); for (const element of elements) { element.addEventListener("submit", function (e) { e.preventDefault(); diff --git a/resources/views/bo/layout.blade.php b/resources/views/bo/layout.blade.php index 58e8b73..6b47d5d 100644 --- a/resources/views/bo/layout.blade.php +++ b/resources/views/bo/layout.blade.php @@ -8,11 +8,24 @@ @include('bo.layouts.header') -
- {{-- Show a message when an action is performed --}} - @include('modules.flash-messages') - {{-- Main content --}} - @yield('content') + +
+
+ +
+
+
+ @include('modules.flash-messages') +
+
+
+ +
+
+ @yield('content') +
+
+
diff --git a/resources/views/bo/layouts/footer.blade.php b/resources/views/bo/layouts/footer.blade.php index c32877c..9a1e2cd 100644 --- a/resources/views/bo/layouts/footer.blade.php +++ b/resources/views/bo/layouts/footer.blade.php @@ -1,10 +1,8 @@ -