Compare commits

..

14 commits

Author SHA1 Message Date
Noah Lancien
bd85c81259 fix: mr review 2026-06-09 15:38:19 +02:00
Noah Lancien
7456b31bf3 fix: remove relation between media and users + update 'historiques' lines to make user nullable 2026-06-09 15:38:03 +02:00
Noah Lancien
99209888a9 fix: ♻️ use user staticRule atLeastRole instead of comparison for roles 2026-06-09 15:16:58 +02:00
Noah Lancien
28993a5db5 feat: ✨ Updated user roles - #34 2026-06-04 17:17:38 +02:00
karl.muller
8941dbacdd Merge branch '4-tags' into 'develop'
feat: ✨ Suggest existing tags in media edition

See merge request ldc-web/projets/dam!14
2026-05-21 13:58:09 +00:00
Noah Lancien
2301d68434 feat: ✨ Suggest existing tags in media edition 2026-05-13 17:36:41 +02:00
Noix
3bc7a371df Merge branch '33-revert-version-commits' into 'develop'
revert: ⏪ revert a73bcc2b4b and...

See merge request ldc-web/projets/dam!11
2026-05-13 15:28:33 +00:00
Noix
4f5fea4436 Merge branch '32-new-columns' into 'develop'
feat: ✨ Added AnglePriseVue + NatureFichier + TypeContenu

See merge request ldc-web/projets/dam!12
2026-05-13 15:28:17 +00:00
Noix
99a57143b6 Merge branch '25-brand-relations-edition' into 'develop'
feat: ✨ Added edition page for brands <-> users relations

See merge request ldc-web/projets/dam!13
2026-05-13 15:27:55 +00:00
Noah Lancien
f3060ed3bf feat: ✨ order brands by libMarq in brands.index 2026-05-13 17:17:21 +02:00
Noah Lancien
91c0aa2f9a feat: ✨ Added edition page for brands <-> users relations 2026-05-13 16:53:56 +02:00
Noah Lancien
a0c2cd8db6 fix: 🐛 prefix new tables with arcadia_ + added a condition to edit new columns only when subject === 1 2026-05-12 13:00:44 +02:00
Noah Lancien
e5d497fa76 feat: ✨ Added AnglePriseVue + NatureFichier + TypeContenu 2026-05-12 10:32:12 +02:00
Noah Lancien
c2978f6ab1 revert: ⏪ revert a73bcc2b4b and 20e5a325ff 2026-05-06 09:29:16 +02:00
58 changed files with 2014 additions and 611 deletions

View file

@ -34,3 +34,46 @@ php artisan key:generate
```
MAIL_VERIFY_PEER=false
```
# DOCUMENTATION
## Rôles (06/2026)
| Ancienne Valeur | Label | Nouvelle valeur |
| --------------- | ----- | --------------- |
| X | CONCEPTEUR | 1 |
| 1 | ADMIN | 2 |
| X | CONTRIBUTEUR ARCADIA | 3 |
| 4 | CONTRIBUTEUR | 4 |
| 3 | CONTRIBUTEUR RESTREINT | 5 |
| 2 | LECTEUR | 6 |
Actions restreintes au role Concepteur :
- Partie paramétrage :
- Récupération des médias
- Anomalies lien Arcadia
Actions restreintes au role Admin (ou plus) :
- Partie paramétrage :
- Gestion des marques
- Gestion des dossiers
- Gestion des utilisateurs
Actions restreintes au role Contributeur Arcadia (ou plus) :
- Création lien Arcadia
Actions restreintes au role Contributeur (ou plus) :
- Déplacer média non lié à Arcadia
- Supprimer média non lié à Arcadia
Actions restreintes au role Contributeur restreint (ou plus) :
- Mise à jour média
- Duplication média
- Appliquer étiquette à un média
Règles sur les requêtes :
- Dossiers : Tous pour concepteur et admin, sinon tous les non brouillons ou liés à une marque du user
- Medias : Tous pour concepteur et admin, sinon ceux liés à une marque du user
- Compteurs affichés à côté des dossier : Tous les médias pour les admins, sinon tous les non brouillons ou liés à une marque du user
- Ajout dossier "Dépôt de fichiers" pour les admins
- A la suppression d'un media : Possible sur tous les medias pour les admins, seulement sur ceux non liés à arcadia pour les autres

View file

@ -62,6 +62,7 @@ class ReindexElasticMedia extends Command
/** @var \App\Models\Media $media */
foreach ($mediaCollection->getCollection() as $media) {
/** @throws \RuntimeException */
$media->marque_ids = $media->marques()->pluck('codMarq')->toArray();
$media->addToElasticSearch();
$bar->advance();
}

View file

@ -0,0 +1,51 @@
<?php
namespace App\Enums\Users;
use App\Traits\Enums\BaseEnum;
/**
* WARNING: If any role is removed or added, please edit the js class (resources/js/role-enum.js).
*/
enum RoleEnum: int
{
use BaseEnum;
case conceptor = 1;
case admin = 2;
case contributorarcadia = 3;
case contributor = 4;
case contributorlimited = 5;
case reader = 6;
/**
* Optionnal labels definition.
*
* @phpstan-ignore-next-line
*/
private const LABELS = [
self::conceptor->name => 'enums.user-role.conceptor',
self::admin->name => 'enums.user-role.admin',
self::contributorarcadia->name => 'enums.user-role.contributorarcadia',
self::contributor->name => 'enums.user-role.contributor',
self::contributorlimited->name => 'enums.user-role.contributorlimited',
self::reader->name => 'enums.user-role.reader',
];
/**
* Get cases for form select input.
*
* @return array
*/
public static function getFormCases(): array
{
$user = auth()->user();
if (isset($user) && !$user->role->equals(self::conceptor)) {
return array_filter(
self::cases(),
fn($case) => $case !== self::conceptor
);
}
return self::cases();
}
}

View file

@ -7,6 +7,14 @@ use Illuminate\Contracts\Database\Eloquent\Builder;
class ArcadiaLinkController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(ArcadiaLink::class);
}
/**
* Display a listing of the resource.
*
@ -14,9 +22,6 @@ class ArcadiaLinkController extends Controller
*/
public function index()
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
// * ArcadiaLink->Media is nullable
$arcadiaLinkList = ArcadiaLink::query()

View file

@ -0,0 +1,58 @@
<?php
namespace App\Http\Controllers;
use App\Models\Marque;
use App\Http\Requests\Bo\UpdateBrandUserRequest;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class BrandController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Marque::class);
}
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$brands = Marque::query()->orderBy('libMarq')->get();
return view('brands.index')->with([
'brands' => $brands
]);
}
public function editUsers(Marque $brand)
{
Gate::authorize('updateUsers', Marque::class);
return view('brands.edit-relations')->with([
'brand' => $brand,
'brands' => Marque::all(),
'userModels' => User::all(),
]);
}
public function updateUsers(UpdateBrandUserRequest $request, Marque $brand)
{
Gate::authorize('updateUsers', Marque::class);
return DB::transaction(function () use ($request, $brand) {
if ($request->validated('users')) {
$brand->users()->sync($request->validated('users'));
return redirect()->route('brands.editUsers', ['brand' => $brand])
->with('success', __('Les utilisateurs ont bien été mis à jour'));
}
return redirect()->back()->with('error', __('Une erreur est survenue. Veuillez réessayer ultérieurement.'));
});
}
}

View file

@ -3,6 +3,7 @@
namespace App\Http\Controllers;
use App\Enums\MediaStatut;
use App\Enums\Users\RoleEnum;
use App\Http\Requests\Bo\Ajax\FetchFoldersRequest;
use App\Http\Requests\Bo\Ajax\LoadFoldersRequest;
use App\Http\Requests\Bo\Folders\StoreDepotFolderRequest;
@ -15,6 +16,7 @@ use App\Models\Marque;
use App\Models\Media;
use App\Models\Variante;
use App\Models\Format;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
@ -25,6 +27,14 @@ use Illuminate\Support\Facades\DB;
class DossierController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(Dossier::class);
}
/**
* Ajax Load folders
*
@ -55,7 +65,7 @@ class DossierController extends Controller
->select($select);
// ? Is admin ?
if (!$user->hasRole(config('roles.const.ADMIN'))) {
if (!UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
// * If not admin, get using brands.
/** @var \Illuminate\Database\Eloquent\Collection<integer> Codes marques */
$marques = $user->marques()->pluck('codMarq');
@ -71,7 +81,7 @@ class DossierController extends Controller
->sortBy('favoris')->values();
// ? Is admin ?
if ($user->hasRole(config('roles.const.ADMIN'))) {
if (UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
// * Ajout du dépôt de fichier pour les admin.
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> Dossiers depots */
$depotQuery = Dossier::query()
@ -89,12 +99,11 @@ class DossierController extends Controller
->whereHas('sujet', fn (Builder $q) => $q->where('codeSujet', $request->input('sujet_id')))
->whereDoesntHave('dossier');
// ? Is not admin ?
if (!$user->hasRole(config('roles.const.ADMIN'))) {
if (!UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
$racineCountQuery->where(
fn (Builder $q) =>
$q->where('statut', MediaStatut::public_etendu)
->orWhere('statut', MediaStatut::public)
->orWhere('created_by', $user->id)
);
}
Arr::set($folders, 'racineCount', $racineCountQuery->count());
@ -127,7 +136,7 @@ class DossierController extends Controller
->where('system', true)->get();
$response['formats'] = $FormatsSujet->merge($FormatsSystem);
$response['draggable'] = $user->hasRole([config('roles.const.ADMIN'), config('roles.const.REDACTEUR')]);
$response['draggable'] = UserStaticRules::atLeastRole($user, RoleEnum::admin);
// Envoi du role .
$response['role'] = $user->role;
@ -144,10 +153,6 @@ class DossierController extends Controller
*/
public function index()
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Dossier> */
$dossiers = Dossier::query()
->whereNull('parent_id')
@ -182,10 +187,6 @@ class DossierController extends Controller
]);
}
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
$marques = Marque::all();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> */
@ -205,11 +206,7 @@ class DossierController extends Controller
*/
public function store(StoreFolderRequest $request)
{
DB::transaction(function () use ($request) {
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
return DB::transaction(function () use ($request) {
// ---- Le dossier
$dossier = new Dossier();
$dossier->fill($request->validated());
@ -230,8 +227,8 @@ class DossierController extends Controller
return redirect()->route('dossier.edit', ['dossier' => $dossier])
->with('success', 'Le dossier a été enregistré');
}
return redirect()->back()->with('error', 'Erreur lors de la création');
});
return back();
}
/**
@ -242,10 +239,6 @@ class DossierController extends Controller
*/
public function edit(Dossier $dossier)
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
$marques = Marque::all();
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Marque> */
$dossierMarques = $dossier->marques;
@ -271,11 +264,7 @@ class DossierController extends Controller
*/
public function update(UpdateFolderRequest $request, Dossier $dossier): RedirectResponse
{
DB::transaction(function () use ($request, $dossier) {
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
return DB::transaction(function () use ($request, $dossier) {
// ---- Le dossier
$dossier->fill($request->validated());
@ -287,8 +276,8 @@ class DossierController extends Controller
return redirect()->route('dossier.edit', ['dossier' => $dossier])
->with('success', trans('Le dossier a été mis à jour'));
}
return redirect()->back()->with('error', 'Erreur lors de la mise à jour');
});
return back();
}
/**
@ -299,14 +288,11 @@ class DossierController extends Controller
*/
public function destroy(Dossier $dossier): RedirectResponse
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
if ($dossier->deleteOrFail()) {
return \response()->redirectToRoute('dossier.index')
return redirect()->route('dossier.index')
->with('warning', trans('Le dossier à été supprimé'));
}
return back();
return redirect()->back()->with('error', 'Erreur lors de la suppression');
}
/**

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Enums\MediaExtension;
use App\Enums\MediaStatut;
use App\Enums\Users\RoleEnum;
use App\Events\MediaLinkWithArcadia;
use App\Events\MediaMoved;
use App\Events\MediaSearched;
@ -18,6 +19,9 @@ use App\Http\Requests\Bo\LoadMediaRequest;
use App\Http\Requests\Bo\ShowInterfaceRequest;
use App\Lib\ArcadiaLinkUtilities;
use App\Lib\ToolboxHelper;
use App\Models\ArcadiaAnglePriseVue;
use App\Models\ArcadiaNatureFichier;
use App\Models\ArcadiaTypeContenu;
use App\Models\Article;
use App\Models\Copyright;
use App\Models\Dossier;
@ -33,6 +37,7 @@ use App\Models\Societe;
use App\Models\Sujet;
use App\Models\Tag;
use App\Models\TypeMedia;
use App\Policies\Rules\UserStaticRules;
use App\Traits\Http\HandlesMedia;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\ModelNotFoundException;
@ -42,6 +47,7 @@ use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
@ -73,11 +79,15 @@ class MediaController extends Controller
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Societe> */
$societes = Societe::orderBy('libelle')->get();
$typesmedia = TypeMedia::all();
$langues = Langue::all();
$copyrights = Copyright::all();
$etiquettes = Etiquette::all();
$marques = Marque::all();
$typesmedia = TypeMedia::all();
$langues = Langue::all();
$copyrights = Copyright::all();
$etiquettes = Etiquette::all();
$marques = Marque::all();
$naturesfichier = ArcadiaNatureFichier::all();
$typescontenu = ArcadiaTypeContenu::all();
$anglesprisevue = ArcadiaAnglePriseVue::all();
$tags = Tag::query()->orderBy('libelle')->get();
/** @var int|null */
$dossier_id = $request->d;
@ -122,6 +132,10 @@ class MediaController extends Controller
return view('media_manager')->with([
'sujets' => $sujets,
'typesmedia' => $typesmedia,
'naturesfichier' => $naturesfichier,
'typescontenu' => $typescontenu,
'anglesprisevue' => $anglesprisevue,
'tags' => $tags,
'langues' => $langues,
'copyrights' => $copyrights,
'etiquettes' => $etiquettes,
@ -237,10 +251,7 @@ class MediaController extends Controller
$sort
));
Arr::set($response, 'draggable', $user->hasRole([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR')
]));
Arr::set($response, 'draggable', UserStaticRules::atLeastRole($user, RoleEnum::contributor));
Arr::set($response, 'isDepot', $isDepot);
Arr::set(
$response,
@ -249,7 +260,7 @@ class MediaController extends Controller
strpos($searchText, '=') !== false &&
\strval(collect(explode('=', $searchText))->get(0)) === 'ca'
);
Arr::set($response, 'role', $user->role);
Arr::set($response, 'role', $user->role->value);
return \response()->json($response);
}
@ -262,14 +273,11 @@ class MediaController extends Controller
*/
public function moveMedia(MoveMediaRequest $request): JsonResponse
{
Gate::authorize('move', Media::class);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR')
]);
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaList = Media::whereIn('id', collect($request->mediaList)->pluck('id'))
->where('isLocked', false)->get();
@ -299,16 +307,14 @@ class MediaController extends Controller
*/
public function removeMedia(DeleteMediaRequest $request): JsonResponse
{
$deletedMediasList = [];
Gate::authorize('delete', Media::class);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR')
]);
if ($user->hasRole(config('roles.const.ADMIN'))) {
$deletedMediasList = [];
if (UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaCollection = Media::whereIn('id', $request->mediaList)->get();
} else {
@ -334,6 +340,8 @@ class MediaController extends Controller
*/
public function recoverMedia(Request $request): JsonResponse
{
Gate::authorize('recover', Media::class);
try {
return DB::transaction(function () use ($request) {
/** @var \App\Models\Media */
@ -368,14 +376,7 @@ class MediaController extends Controller
*/
public function updateMedia(UpdateMediaRequest $request, Media $media): JsonResponse
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR'),
config('roles.const.CONTRIBUTEUR')
]);
Gate::authorize('update', Media::class);
if ($media->isLocked) {
return \response()->json([
@ -439,16 +440,12 @@ class MediaController extends Controller
*/
public function duplicateMedia(Media $media): JsonResponse
{
Gate::authorize('duplicate', Media::class);
return DB::transaction(function () use ($media) {
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR'),
config('roles.const.CONTRIBUTEUR')
]);
if ($media->isLocked) {
return \response()->json([
'status' => false,
@ -491,7 +488,6 @@ class MediaController extends Controller
}
$clonedMedia->isDefault = true;
$clonedMedia->owner()->associate($user);
$clonedMedia->typeMedia()->associate(TypeMedia::getMediaType($clonedMedia->extension));
@ -541,14 +537,7 @@ class MediaController extends Controller
*/
public function applySticker(ApplyStickerRequest $request): JsonResponse
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([
config('roles.const.ADMIN'),
config('roles.const.REDACTEUR'),
config('roles.const.CONTRIBUTEUR')
]);
Gate::authorize('applySticker', Media::class);
/** @var \Illuminate\Database\Eloquent\Collection<\App\Models\Media> */
$mediaList = Media::whereIn('id', collect($request->mediaList)->pluck('id'))->get();
@ -580,10 +569,7 @@ class MediaController extends Controller
*/
public function checkRefKey(CheckRefKeyRequest $request): JsonResponse
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([config('roles.const.ADMIN')]);
Gate::authorize('linkToArcadia', Media::class);
/** @var \App\Models\Media */
$media = $request->media;
@ -649,11 +635,11 @@ class MediaController extends Controller
*/
public function createArcadiaLink(Media $media): JsonResponse
{
Gate::authorize('linkToArcadia', Media::class);
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles([config('roles.const.ADMIN')]);
if ($media->arcadia_id) {
throw ValidationException::withMessages([
'media' => trans('Ce média est déjà lié à arcadia')
@ -906,7 +892,9 @@ class MediaController extends Controller
$esShould = array_merge($esShould, [
\array_undot(['match.statut' => MediaStatut::public_etendu]),
\array_undot(['match.statut' => MediaStatut::public]),
\array_undot(['match.created_by' => $user->id])
\array_undot([
'terms.marque_ids' => $user->marques->pluck('codMarq')->all()
])
]);
return;
} //end if
@ -923,7 +911,7 @@ class MediaController extends Controller
]]);
// ? User n'est pas ADMIN ?
if (!$user->hasRole(config('roles.const.ADMIN'))) {
if (!UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
// * Ajout de restrictions
// * Tout ce qui nous appartient et tout ce qui n'est pas brouillon
$esMust = array_merge($esMust, [
@ -939,10 +927,6 @@ class MediaController extends Controller
]],
'bool.adjust_pure_negative' => true,
'bool.boost' => 1.0
]),
\array_undot([
'term.created_by.value' => $user->id,
'term.created_by.boost' => 1.0
])
],
'bool.adjust_pure_negative' => true,

View file

@ -4,6 +4,7 @@ namespace App\Http\Controllers;
use App\Models\Media;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
class RecoveryController extends Controller
{
@ -14,9 +15,7 @@ class RecoveryController extends Controller
*/
public function index()
{
/** @var \App\Models\User */
$user = auth()->user();
$user->authorizeRoles(config('roles.const.ADMIN'));
Gate::authorize('recover', Media::class);
return view('recovery.index');
}
@ -29,6 +28,8 @@ class RecoveryController extends Controller
*/
public function checkArcadiaId(Request $request)
{
Gate::authorize('linkToArcadia', Media::class);
return \response()->json(
Media::onlyTrashed()->where('arcadia_id', '=', \intval($request->id))->exists()
);

View file

@ -293,7 +293,6 @@ class UploadController extends Controller
// * Si on upload un média pour remplacer un fichier par un autre
if ($isNewMedia) {
$media->libelle = Str::limit($sanitizedFileBasename, 50, '');
$media->owner()->associate($user);
$media->dossier()->associate($dossier);
$media->sujet()->associate($sujet);
// * Par défaut FR

View file

@ -7,13 +7,23 @@ use App\Models\Marque;
use App\Models\Sujet;
use Illuminate\Http\Request;
use Adldap\Laravel\Facades\Adldap;
use App\Http\Requests\DestroyUserRequest;
use App\Enums\Users\RoleEnum;
use App\Http\Requests\Bo\Users\StoreUserRequest;
use App\Http\Requests\Bo\Users\UpdateUserRequest;
use App\Models\Dossier;
use App\Models\Media;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
class UserController extends Controller
{
/**
* Create the controller instance.
*/
public function __construct()
{
$this->authorizeResource(User::class);
}
/**
* Display a listing of the resource.
*
@ -21,9 +31,6 @@ class UserController extends Controller
*/
public function index()
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$users = User::all();
return view('users.index')->with([
'users' => $users
@ -37,14 +44,10 @@ class UserController extends Controller
*/
public function create()
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$roles = config('roles.label');
$marques = Marque::orderBy('libMarq', 'asc')->get();
$sujets = Sujet::where('sujet_dam', true)->orderBy('libelle', 'asc')->get();
return view('users.create')->with([
'roles' => $roles, 'marques' => $marques,
'marques' => $marques,
'sujets' => $sujets
]);
}
@ -52,67 +55,24 @@ class UserController extends Controller
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Http\Requests\Bo\Users\StoreUserRequest $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
public function store(StoreUserRequest $request)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$validatedData = $request->validate([
'matricule' => 'required',
'firstname' => 'required',
'lastname' => 'required',
'email' => 'email',
'role' => 'required'
]);
return DB::transaction(function () use ($request) {
$user = new User();
$user->fill($request->validated());
$user = User::where('matricule', $request->matricule)->first();
if ($user) {
session()->flash('error', "Ce matricule est déjà présent dans l'application. Ajout impossible.");
return redirect()->route('user.create');
} else {
// ---- L'utilisateur
$user = new User();
$user->matricule = $request->matricule;
$user->firstname = $request->firstname;
$user->lastname = $request->lastname;
$user->email = $request->email;
$user->role = $request->role;
if ($user->saveOrFail()) {
$user->marques()->sync($request->validated('marques'));
$user->sujets()->sync($request->validated('sujets'));
if ($user->save()) {
// ---- Les marques
$sMarques = $request->marques;
if ($sMarques != "") {
$marques = explode(",", $sMarques);
foreach ($marques as $marqueId) {
$user->marques()->attach($marqueId);
}
}
// ---- Les sujets
$sSujets = $request->sujets;
if ($sSujets != "") {
$sujets = explode(",", $sSujets);
foreach ($sujets as $sujetId) {
$user->sujets()->attach($sujetId);
}
} else {
session()->flash('error', "Vous devez ajouter au moins un sujet.");
return redirect()->route('user.create');
}
session()->flash('success', "L'utilisateur a été enregistré");
return redirect()->route(
'user.edit',
['user' => $user]
);
} else {
session()->flash('error', "Une erreur s'est produite, ajout impossible");
return redirect()->route('user.create');
} //end if
}//end if
return redirect()->route('user.edit', ['user' => $user])
->with('success', 'L\'utilisateur a été enregistré');
}
return redirect()->back()->with('error', 'Une erreur s\'est produite, ajout impossible');
});
}
/**
@ -123,15 +83,12 @@ class UserController extends Controller
*/
public function checkMatricule(Request $request)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
Gate::authorize('checkMatricule', User::class);
$response = [];
$response["exist_bdd"] = false;
$response["exist_ldap"] = false;
// This will get all the request data.
$data = $request->all();
$matricule = $request->input('matricule');
// ---- Ctrl de la présence du matricule en base
@ -163,11 +120,10 @@ class UserController extends Controller
*/
public function show(User $user)
{
$roles = config('roles.label');
$marques = $user->marques;
$sujets = $user->sujets;
return view('users.show')->with([
'user' => $user, 'roles' => $roles,
'user' => $user,
'marques' => $marques,
'sujets' => $sujets
]);
@ -181,117 +137,73 @@ class UserController extends Controller
*/
public function edit(User $user)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$roles = config('roles.label');
$userMarques = $user->marques;
$marques = Marque::orderBy('libMarq', 'asc')->get();
$userSujets = $user->sujets;
$sujets = Sujet::where('sujet_dam', true)->orderBy('libelle', 'asc')->get();
$users = User::select(['id', 'email'])
->whereIn('role', [
config('roles.const.ADMIN'), config('roles.const.CONTRIBUTEUR'),
config('roles.const.REDACTEUR')
$users = User::query()
->select(['id', 'email'])
->whereNotIn('role', [
RoleEnum::reader
])
->where('id', '!=', $user->id)
->orderBy('email')
->get();
return view('users.edit')->with([
'user' => $user, 'roles' => $roles, 'users' => $users,
'userMarques' => $userMarques, 'marques' => $marques,
'userSujets' => $userSujets, 'sujets' => $sujets
'user' => $user,
'users' => $users,
'userMarques' => $userMarques,
'marques' => $marques,
'userSujets' => $userSujets,
'sujets' => $sujets
]);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param \App\Models\User $user
* @param \App\Http\Requests\Bo\Users\UpdateUserRequest $request
* @param \App\Models\User $user
* @return \Illuminate\Http\Response
*/
public function update(Request $request, User $user)
public function update(UpdateUserRequest $request, User $user)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
$validatedData = $request->validate([
'firstname' => 'required',
'lastname' => 'required',
'email' => 'email',
'role' => 'required'
]);
return DB::transaction(function () use ($request, $user) {
$user->fill($request->validated());
// ---- L'utilisateur
$user->firstname = $request->firstname;
$user->lastname = $request->lastname;
$user->email = $request->email;
$user->role = $request->role;
$user->save();
if ($user->saveOrFail()) {
$user->marques()->sync($request->validated('marques'));
$user->sujets()->sync($request->validated('sujets'));
// ---- Les marques
$user->marques()->detach();
$sMarques = $request->marques;
if ($sMarques != "") {
$marques = explode(",", $sMarques);
foreach ($marques as $marqueId) {
$user->marques()->attach($marqueId);
return redirect()->route('user.edit', ['user' => $user])
->with('success', 'L\'utilisateur a été mis à jour');
}
}
// ---- Les Sujets
$user->sujets()->detach();
$sSujets = $request->sujets;
if ($sSujets != "") {
$sujets = explode(",", $sSujets);
foreach ($sujets as $sujetId) {
$user->sujets()->attach($sujetId);
}
} else {
session()->flash('error', "Vous devez ajouter au moins un sujet.");
return redirect()->route('user.edit', ['user' => $user]);
}
session()->flash('success', "L'utilisateur a été mis à jour");
return redirect()->route('user.edit', ['user' => $user]);
return redirect()->back()->with('error', 'Une erreur s\'est produite, modification impossible');
});
}
/**
* Remove the specified resource from storage.
*
* @param \App\Http\Requests\DestroyUserRequest $request
* @param \Illuminate\Http\Request $request
* @param \App\Models\User $user
* @return \Illuminate\Http\Response
*/
public function destroy(DestroyUserRequest $request, User $user)
public function destroy(Request $request, User $user)
{
/** @var \App\Models\User $localUser */
$localUser = auth()->user();
$localUser->authorizeRoles(config('roles.const.ADMIN'));
/** @var \App\Models\User */
$userPourRemplacer = User::whereId($request->created_by)->firstOrFail();
try {
return DB::transaction(function () use ($user, $userPourRemplacer) {
Media::whereCreatedBy($user->id)->get()
->map(function (Media $media) use ($userPourRemplacer) {
$media->owner()->associate($userPourRemplacer);
$media->saveOrFail();
});
return DB::transaction(function () use ($user) {
if ($user->deleteOrFail()) {
session()->flash('success', trans('L\'utilisateur à été supprimé'));
return redirect(route('user.index'));
return redirect()
->route('user.index')
->with('success', trans('L\'utilisateur à été supprimé'));
}
session()->flash('error', 'Erreur lors de la suppression');
return redirect()->back();
return redirect()->back()->with('error', 'Erreur lors de la suppression');
});
} catch (\Exception $e) {
report($e);
session()->flash('error', 'Erreur lors de la suppression');
return redirect()->back();
return redirect()->back()->with('error', 'Erreur lors de la suppression');
}//end try
}

View file

@ -61,9 +61,13 @@ class UpdateMediaRequest extends FormRequest
// Article.
if ($this->sujet_id == 1) {
$rules['articles'] = 'present|array|min:0';
$rules['articles.*'] = 'array';
$rules['articles.*.id'] = 'exists:articles,id';
$rules['articles'] = 'present|array|min:0';
$rules['articles.*'] = 'array';
$rules['articles.*.id'] = 'exists:articles,id';
$rules['arcadia_angle_prise_vue_id'] = 'required|exists:arcadia_angle_prise_vues,id';
$rules['arcadia_nature_fichier_id'] = 'required|exists:arcadia_nature_fichiers,id';
$rules['arcadia_type_contenu_id'] = 'required|exists:arcadia_type_contenus,id';
}
// Marque.
if ($this->sujet_id == 2) {

View file

@ -0,0 +1,43 @@
<?php
namespace App\Http\Requests\Bo;
use Illuminate\Foundation\Http\FormRequest;
class UpdateBrandUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return auth()->check();
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'users' => !$this->users ? null : \explode(',', \strval($this->users))
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'users' => 'nullable|array',
'users.*' => 'required|exists:users,id',
];
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
use Illuminate\Validation\Rules\Enum;
class StoreUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('create', User::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
$this->merge([
'marques' => explode(',', $this->input('marques')),
'sujets' => explode(',', $this->input('sujets')),
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return [
'matricule' => 'required|string|unique:users,matricule',
'firstname' => 'required|string',
'lastname' => 'required|string',
'email' => 'required|email:rfc,strict,dns,spoof,filter|unique:users,email',
'role' => [
'required',
new Enum(RoleEnum::class),
Rule::in(array_column(RoleEnum::getFormCases(), 'value'))
],
'marques' => 'nullable|array',
'marques.*' => 'nullable|integer|exists:marques,codMarq',
'sujets' => 'required|array',
'sujets.*' => 'required|integer|exists:sujets,codeSujet',
];
}
}

View file

@ -0,0 +1,58 @@
<?php
namespace App\Http\Requests\Bo\Users;
use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Validation\Rule;
class UpdateUserRequest extends StoreUserRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize(): bool
{
return Gate::check('update', User::class);
}
/**
* Prepare the data for validation.
*
* @return void
*/
protected function prepareForValidation()
{
parent::prepareForValidation();
// Set matricule because it is required and not editable.
/** @var \App\Models\User $userModel */
$userModel = request()->route()->parameter('user');
$this->merge([
'matricule' => $userModel->matricule,
]);
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, mixed>
*/
public function rules()
{
return array_merge(parent::rules(), [
'email' => [
'required',
'email:rfc,strict,dns,spoof,filter',
Rule::unique('users', 'email')->ignore(\optional($this->route('user'))->getRouteKey())
],
'matricule' => [
'required',
'string',
Rule::unique('users', 'matricule')->ignore(\optional($this->route('user'))->getRouteKey())
],
]);
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace App\Http\Requests;
use App\Rules\HasMinimumRole;
use App\Rules\IsValidRole;
use App\Models\User;
use Illuminate\Foundation\Http\FormRequest;
class DestroyUserRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* @return boolean
*/
public function authorize()
{
$this->merge([
'minimumrole' => optional(User::whereId($this->created_by)->first())->role ?? null
]);
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'created_by' => 'required|exists:users,id',
'minimumrole' => [new IsValidRole(), new HasMinimumRole('CONTRIBUTEUR')]
];
}
/**
* Get custom attributes for validator errors.
*
* @return array
*/
public function attributes()
{
return [
'minimumrole' => trans('rôle')
];
}
}

View file

@ -39,9 +39,7 @@ class XmlArcadiaMediasIn extends XmlJobs
*
* @var array
*/
protected $nullableFields = [
'version'
];
protected $nullableFields = [];
/**
* Fields that are unique
@ -71,7 +69,7 @@ class XmlArcadiaMediasIn extends XmlJobs
$media = Media::withTrashed()->where($uniqueColumnsKey)->firstOrFail();
$media->arcadia_id = $line['arcadia_id'];
$media->version = $line['version'] ?? 1;
$media->version = $line['version'];
$media->isLocked = false;
$media->saveOrFail();

View file

@ -217,6 +217,12 @@ class ArcadiaLinkUtilities
->appendChild($dom->createCDATASection("$media->copyright_id"));
$root->appendChild($dom->createElement('codeVariante'))
->appendChild($dom->createCDATASection("$media->variante_id"));
$root->appendChild($dom->createElement('codeAnglePriseVue'))
->appendChild($dom->createCDATASection($media->arcadiaAngleprisevue->value));
$root->appendChild($dom->createElement('codeNatureFichier'))
->appendChild($dom->createCDATASection($media->arcadiaNaturefichier->value));
$root->appendChild($dom->createElement('codeTypeContenu'))
->appendChild($dom->createCDATASection($media->arcadiaTypecontenu->value));
$root->appendChild($dom->createElement('dateCreation'))
->appendChild($dom->createCDATASection("$media->created_at"));
$root->appendChild($dom->createElement('dateModification'))

View file

@ -10,6 +10,7 @@ use App\Events\MediaLinkWithArcadia;
use App\Events\MediaMoved;
use App\Events\MediaUpdated;
use App\Models\Historique;
use \Illuminate\Support\Str;
class HistorizeMediaEvent
{
@ -54,10 +55,18 @@ class HistorizeMediaEvent
$description = trans('Ajout de la liaison Arcadia');
}
$historique->user()->associate($event->getUser());
$eventUser = $event->getUser();
$historique->user()->associate($eventUser);
$historique->media()->associate($event->getMedia());
$historique->type = $type;
$historique->description = $description;
$historique->description = sprintf(
'%s - %s %s (%s)',
$description,
$eventUser->firstname,
Str::upper($eventUser->lastname),
$eventUser->matricule
);
if (!$historique->save()) {
throw new \RuntimeException('Failed to save historic to Database');

View file

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ArcadiaAnglePriseVue extends Model
{
protected $fillable = [
'value',
'label'
];
// * RELATIONSHIPS
/**
* HasMany variantes relation
*
* Get the media for the ArcadiaAnglePriseVue.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function mediaList()
{
return $this->hasMany(Media::class);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ArcadiaNatureFichier extends Model
{
protected $fillable = [
'value',
'label'
];
// * RELATIONSHIPS
/**
* HasMany variantes relation
*
* Get the media for the ArcadiaNatureFichier.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function mediaList()
{
return $this->hasMany(Media::class);
}
}

View file

@ -0,0 +1,27 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ArcadiaTypeContenu extends Model
{
protected $fillable = [
'value',
'label'
];
// * RELATIONSHIPS
/**
* HasMany variantes relation
*
* Get the media for the ArcadiaTypeContenu.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function mediaList()
{
return $this->hasMany(Media::class);
}
}

View file

@ -3,7 +3,9 @@
namespace App\Models;
use App\Enums\MediaStatut;
use App\Enums\Users\RoleEnum;
use App\Models\User;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Auth;
@ -71,8 +73,8 @@ class Dossier extends Model
*/
public function getMediacountAttribute(): int
{
/** @var \App\Model\User $user */
$user = Auth::user();
/** @var \App\Models\User */
$user = auth()->user();
// ? Est un dossier dépot ou racine ?
if ($this->parent_id === 1 or $this->id === 1) {
@ -82,15 +84,22 @@ class Dossier extends Model
// * Dossier classic
// ? User is admin ?
if ($user->hasRole(config('roles.const.ADMIN'))) {
if (UserStaticRules::atLeastRole($user, RoleEnum::admin)) {
// * Tout de tout le monde
return $this->mediaList()->count();
}
// * Tous les non brouillons ou crées par le user
// * Tous les non brouillons ou liés à une marque du user.
return $this->mediaList()
->where(fn (Builder $q) => $q->where('statut', '!=', MediaStatut::brouillon)
->orWhere('created_by', $user->id))
->where(
fn (Builder $q) => $q->where('statut', '!=', MediaStatut::brouillon)
->orWhere(fn (Builder $query) => $query
->whereHas('marques', fn ($pivotQuery) => $pivotQuery->whereIn(
'marque_id',
$user->marques->pluck('id')->toArray()
))
)
)
->count();
}

View file

@ -31,7 +31,8 @@ class Marque extends Model
*/
public function users()
{
return $this->belongsToMany(User::class);
// We have to define foreignPivotKey because primaryKey has been overwritten ...
return $this->belongsToMany(User::class, foreignPivotKey:'marque_id');
}
/**

View file

@ -10,7 +10,6 @@ use App\Events\MediaDestroyed;
use App\Events\MediaUpdated;
use App\Lib\ToolboxHelper;
use App\Traits\Models\BindedToElasticSearch;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Database\Eloquent\SoftDeletes;
@ -52,7 +51,6 @@ class Media extends Model
* @var array
*/
protected $with = [
'owner',
'tags',
'etiquette',
'articles',
@ -76,6 +74,9 @@ class Media extends Model
'copyright_id',
'etiquette_id',
'langue_id',
'arcadia_angle_prise_vue_id',
'arcadia_nature_fichier_id',
'arcadia_type_contenu_id',
'commentaire',
'version',
'statut',
@ -204,7 +205,8 @@ class Media extends Model
]
],
'fielddata' => true
]
],
'marque_ids' => ['type' => 'keyword'],
);
/**
@ -223,6 +225,7 @@ class Media extends Model
MediaCreated::dispatch($user, $model);
if (empty($model->isDuplicate)) {
$model->marque_ids = $model->marques()->pluck('codMarq')->toArray();
/** @throws \RuntimeException */
$model->addToElasticSearch();
}
@ -254,6 +257,7 @@ class Media extends Model
MediaChangedLabel::dispatch($user, $model, $oldLabel, $newLabel);
}
$model->marque_ids = $model->marques()->pluck('codMarq')->toArray();
/** @throws \RuntimeException */
$model->addToElasticSearch();
});
@ -451,14 +455,36 @@ class Media extends Model
}
/**
* BelongsTo user relation
* Get the user that owns the media.
* BelongsTo ArcadiaTypeContenu relation
* Get the ArcadiaTypeContenu that belongs to the media.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function owner()
public function arcadiaTypecontenu()
{
return $this->belongsTo(User::class, 'created_by');
return $this->belongsTo(ArcadiaTypeContenu::class, 'arcadia_type_contenu_id');
}
/**
* BelongsTo ArcadiaAnglePriseVue relation
* Get the ArcadiaAnglePriseVue that belongs to the media.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function arcadiaAngleprisevue()
{
return $this->belongsTo(ArcadiaAnglePriseVue::class, 'arcadia_angle_prise_vue_id');
}
/**
* BelongsTo ArcadiaNatureFichier relation
* Get the ArcadiaNatureFichier that belongs to the media.
*
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function arcadiaNaturefichier()
{
return $this->belongsTo(ArcadiaNatureFichier::class, 'arcadia_nature_fichier_id');
}
/**

View file

@ -2,6 +2,7 @@
namespace App\Models;
use App\Enums\Users\RoleEnum;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
@ -15,7 +16,7 @@ use Illuminate\Notifications\Notifiable;
* @property-read \Illuminate\Support\Carbon $email_verified_at Mail verified at date.
* @property string $password Mot de passe.
* @property string $remember_token
* @property integer $role Rôle in config/role; 1 => Admin.
* @property \App\Enums\Users\RoleEnum $role RoleEnum.
* @property-read \Illuminate\Support\Carbon $created_at Created date.
* @property-read \Illuminate\Support\Carbon $updated_at Updated date.
*
@ -25,8 +26,6 @@ use Illuminate\Notifications\Notifiable;
* Dossiers BelongsToMany relationship.
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Sujet> $sujets
* Sujets BelongsToMany relationship.
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Media> $mediaList
* Medias HasMany relationship.
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Historique> $historiques
* Historiques HasMany relationship.
* @property-read \Illuminate\Database\Eloquent\Collection<\App\Models\Recherche> $recherches
@ -47,9 +46,12 @@ class User extends Authenticatable
* @var array<int, string>
*/
protected $fillable = [
'name',
'matricule',
'firstname',
'lastname',
'email',
'password',
'role',
];
/**
@ -69,51 +71,9 @@ class User extends Authenticatable
*/
protected $casts = [
'email_verified_at' => 'datetime',
'role' => RoleEnum::class,
];
// * METHODS
/**
* Authorize role
*
* @param string|array $roles
* @return boolean
*/
public function authorizeRoles($roles): bool
{
if (is_array($roles) and in_array($this->role, $roles)) {
return true;
}
if (is_string($roles) and $this->role == $roles) {
return true;
}
abort(401, 'Cette action n\'est pas autorisée.');
}
/**
* Check one role
*
* @param array|string $roles
* @return boolean
*/
public function hasRole($roles): bool
{
if (is_array($roles)) {
return in_array($this->role, $roles);
}
return $this->role == $roles;
}
/**
* Get role label
*
* @return string|null
*/
public function getRoleLabel(): ?string
{
return config('roles.label.' . $this->role);
}
// * RELATIONSHIPS
/**
@ -149,17 +109,6 @@ class User extends Authenticatable
return $this->belongsToMany(Sujet::class, 'sujet_user', 'user_id', 'sujet_id');
}
/**
* HasMany media relation
* Get the media for the user.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function mediaList()
{
return $this->hasMany(Media::class);
}
/**
* HasMany historiques relation
* Get the historiques for the user.

View file

@ -0,0 +1,65 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Database\Eloquent\Model;
class ArcadiaLinkPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Database\Eloquent\Model;
class DossierPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Database\Eloquent\Model;
class MarquePolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update relations between users and brands.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function updateUsers(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -0,0 +1,87 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Database\Eloquent\Model;
class MediaPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function recover(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::conceptor);
}
/**
* Determine whether the user can link a media to arcadia.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function linkToArcadia(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributorarcadia);
}
/**
* Determine whether the user can move a media.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function move(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributor);
}
/**
* Determine whether the user can update a media.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributorlimited);
}
/**
* Determine whether the user can delete a media.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributor);
}
/**
* Determine whether the user can duplicate a media.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function duplicate(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributor);
}
/**
* Determine whether the user can apply a sticker (etiquette) to a media.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function applySticker(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::contributor);
}
}

View file

@ -0,0 +1,47 @@
<?php
namespace App\Policies\Rules;
use App\Enums\Users\RoleEnum;
use Illuminate\Database\Eloquent\Model;
/**
* Userlevel Policiy Static Rules
*
* @codeCoverageIgnore
*/
abstract class UserStaticRules
{
/**
* Allow everyone.
*
* @return boolean
*/
public static function everyOne(): bool
{
return true;
}
/**
* Allow no one.
*
* @return boolean
*/
public static function noOne(): bool
{
return false;
}
/**
* At least a minimum role
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @param \App\Enums\Users\RoleEnum $minRole
* @return boolean
*/
public static function atLeastRole(Model $authModel, RoleEnum $minRole): bool
{
// phpcs:enable
return isset($authModel->role) && $authModel->role->value() <= $minRole->value();
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace App\Policies;
use App\Enums\Users\RoleEnum;
use App\Policies\Rules\UserStaticRules;
use Illuminate\Database\Eloquent\Model;
class UserPolicy
{
/**
* Determine whether the user can view any models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function viewAny(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can view the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function view(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can create models.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function create(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can update the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function update(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can delete the model.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function delete(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
/**
* Determine whether the user can get datas with an employee registration number in user creation form.
*
* @param \Illuminate\Database\Eloquent\Model $authModel
* @return boolean
*/
public function checkMatricule(Model $authModel): bool
{
return UserStaticRules::atLeastRole($authModel, RoleEnum::admin);
}
}

View file

@ -1,91 +0,0 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class HasMinimumRole implements Rule
{
/**
* The minimum role the user must have
* @var integer
*/
protected $minimumRole;
/**
* Ordre permettant une comparaison
* ! EN DUR est n'est pas la value de arcadia puisqu'il n'a pas de sens.
*
* @var array
*/
protected $roles = [
'ADMIN' => 1, 'REDACTEUR' => 2,
'CONTRIBUTEUR' => 3, 'LECTEUR' => 4
];
/**
* Create a new rule instance.
*
* @param string $role
* @throws \ErrorException If role is unknown.
*/
public function __construct(string $role)
{
if (!in_array(\intval($role), \array_keys($this->roles))) {
throw new \ErrorException("Unknown role $role");
}
$this->minimumRole = \intval($this->roles[$role]);
}
/**
* Convert Arcadia role to usable roles
*
* 1 (ADMIN) => 1
* 2 (LECTEUR) => 4
* 3 (ADMIN) => 3
* 4 (REDACTEUR) => 2
*
* Donc :
* 1 (ADMIN)
* 2 (REDACTEUR)
* 3 (ADMIN)
* 4 (LECTEUR)
*
* @param integer $value
* @return integer
*/
private function translateRoleValue(int $value): int
{
$translation = [2 => 4, 4 => 2];
return in_array($value, $translation) ? $translation[$value] : $value;
}
/**
* Determine if the validation rule passes.
*
* @param mixed $attribute
* @param mixed $value
* @return boolean
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/
public function passes($attribute, $value)
{
// phpcs:enable
if (!\ctype_digit(strval($value))) {
return false;
};
return $this->translateRoleValue(\intval($value)) <= $this->minimumRole;
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'L\'attribut :attribute :input n\'est pas suffisament élevé.';
}
}

View file

@ -1,36 +0,0 @@
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class IsValidRole implements Rule
{
/**
* Determine if the validation rule passes.
*
* @param mixed $attribute
* @param mixed $value
* @return boolean
* @phpcs:disable Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceBeforeLastUsed
* @phpcs:disable Squiz.Commenting.FunctionComment.TypeHintMissing
*/
public function passes($attribute, $value)
{
// phpcs:enable
if (!\ctype_digit(strval($value))) {
return false;
};
return in_array(\strval($value), config('roles.const'));
}
/**
* Get the validation error message.
*
* @return string
*/
public function message()
{
return 'L\'attribut :attribute n\'est pas valide.';
}
}

View file

@ -0,0 +1,283 @@
<?php
namespace App\Traits\Enums;
use Illuminate\Support\Str;
/**
* Provides rich functionality to PHP backed enums.
*/
trait BaseEnum
{
// * GENERIC METHODS.
/**
* Get enum value.
*
* @return string|integer
*/
public function value(): string|int
{
return self::toValues()[$this->name];
}
/**
* Get enum label.
*
* @return string
*/
public function label(): string
{
return self::toLabels()[$this->name];
}
/**
* Get enum name.
*
* @return string
*/
public function name(): string
{
return $this->name;
}
// * STATIC FETCH METHODS.
/**
* Get enum labels.
*
* @return array<string, string>
* @throws \Error If missing definition label.
* @phpstan-ignore missingType.iterableValue
*/
public static function toLabels(): array
{
if (empty(self::getCache('labels'))) {
$labels = self::getLabelsConstant();
self::setCache('labels', \collect(self::cases())
->mapWithKeys(function ($enum) use ($labels) {
if (!\is_null($labels) and !isset($labels[$enum->name])) {
throw new \Error('Missing definition label ' . self::class . " for {$enum->name}");
}
return [$enum->name => \trans(
!\is_null($labels) ? $labels[$enum->name] : Str::ucfirst($enum->name)
)];
})->all());
}
return self::getCache('labels');
}
/**
* Get enum values.
*
* @return array<string, integer|string>
* @phpstan-ignore missingType.iterableValue
*/
public static function toValues(): array
{
if (empty(self::getCache('values'))) {
self::setCache('values', \collect(self::cases())
->mapWithKeys(fn ($enum) => [$enum->name => $enum->value])->all());
}
return self::getCache('values');
}
/**
* Get enums.
*
* @return list<static>
* @phpstan-ignore missingType.iterableValue
*/
public static function all(): array
{
return self::cases();
}
/**
* Get enums.
*
* @return array<string, object>
* @phpstan-ignore missingType.iterableValue
*/
public static function toArray(): array
{
if (empty(self::getCache('enums'))) {
$definitions = self::getCustomDefinitions();
self::setCache('enums', \collect(self::cases())
->mapWithKeys(function ($enum) use ($definitions) {
$baseArray = [
'name' => $enum->name,
'label' => $enum->label(),
'value' => $enum->value,
];
foreach ($definitions as $index => $elements) {
$index = Str::lower(Str::singular($index));
if (!isset($elements[$enum->name])) {
throw new \Error("Missing definition {$index} " . self::class . " for {$enum->name}");
}
$baseArray[$index] = $elements[$enum->name];
}
return [$enum->name => (object) $baseArray];
})->all());
}
return self::getCache('enums');
}
/**
* Fetch enum using value.
*
* @param self|integer|string $value
* @param string $field
* @return static|null
*/
public static function make(self|int|string $value, string $field = 'value'): self|null
{
if ($field === "label") {
$enumLabels = array_change_key_case(
array_flip(array_map(fn($value): string => Str::slug($value), self::toLabels())),
CASE_LOWER
);
$searchValue = $value instanceof self ? (string) $value->value : (string) $value;
$slugged = Str::slug($searchValue);
$value = $enumLabels[$slugged] ?? false;
if ($value === false) {
return null;
}
$field = 'name';
}
/** @phpstan-ignore-next-line */
return !\is_object($value) ? \collect(self::cases())->keyBy($field)->get($value) : $value;
}
/**
* Test if 2 enums are equals.
*
* @param object ...$others
* @return boolean
* @throws \Error If provided element are not enums that extends BaseEnum.
*/
public function equals(object ...$others): bool
{
foreach ($others as $other) {
if (!\in_array(BaseEnum::class, \class_uses_recursive($other))) {
throw new \Error(__METHOD__ . ' requires $enum to use ' . BaseEnum::class);
}
if (
get_class($this) === get_class($other)
&& $this->value === $other->value // @phpstan-ignore-line
) {
return true;
}
}
return false;
}
/**
* Serialize to json.
*
* @return mixed
*/
public function jsonSerialize(): mixed
{
return $this->value;
}
// * CACHE.
/**
* Get cache array.
*
* @param string $index
* @return array<mixed>
* @phpstan-ignore missingType.iterableValue
*/
private static function getCache(string $index): array
{
self::initCache();
if (!isset($GLOBALS[self::class]['props'][$index])) {
$GLOBALS[self::class]['props'][$index] = [];
}
return $GLOBALS[self::class]['props'][$index];
}
/**
* Set cache array.
*
* @param string $index
* @param array<mixed> $items
* @return void
* @phpstan-ignore missingType.iterableValue
*/
private static function setCache(string $index, array $items): void
{
self::initCache();
if (!isset($GLOBALS[self::class]['props'][$index])) {
$GLOBALS[self::class]['props'][$index] = [];
}
$GLOBALS[self::class]['props'][$index] = $items;
}
/**
* Init cache if needed.
*
* @return void
*/
private static function initCache(): void
{
if (!isset($GLOBALS[self::class])) {
$GLOBALS[self::class] = [
'props' => [
'enums' => [],
'values' => [],
'labels' => []
],
'customProps' => null
];
}
}
// * PRIVATE.
/**
* Fetch the LABELS constant if exists.
*
* @return array<string, string>|null
* @phpstan-ignore missingType.iterableValue
*/
private static function getLabelsConstant(): array|null
{
$class_name = self::class;
try {
$constant_reflex = new \ReflectionClassConstant($class_name, 'LABELS');
return $constant_reflex->getValue();
} catch (\ReflectionException $e) {
return null;
}
}
/**
* Get custom constants.
*
* @return array<string, array<mixed>>
* @phpstan-ignore missingType.iterableValue
*/
private static function getCustomDefinitions(): array
{
if (!isset($GLOBALS[self::class]) or \is_null($GLOBALS[self::class]['customProps'])) {
$names = \collect(self::cases())->keyBy('name');
$GLOBALS[self::class]['customProps'] = \collect((new \ReflectionClass(self::class))->getConstants())
->filter(function (mixed $constant, string $key) use ($names): bool {
if ($key === 'LABELS' or !\is_array($constant)) {
return false;
}
$count = \collect($constant)->keys()
->filter(fn (string $name) => $names->has($name))
->count();
return $count > 0;
})->all();
}
return $GLOBALS[self::class]['customProps'];
}
}

View file

@ -1,17 +0,0 @@
<?php
// TODO: Pass this as an enum.
return [
'const' => [
'ADMIN' => '1',
'LECTEUR' => '2',
'CONTRIBUTEUR' => '3',
'REDACTEUR' => '4',
],
'label' => [
'1' => 'Administrateur',
'2' => 'Lecteur',
'3' => 'Contributeur',
'4' => 'Rédacteur',
]
];

View file

@ -0,0 +1,68 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// Remove 'created_by' column in media.
Schema::table('media', function (Blueprint $table) {
$table->dropForeign('FK_medias_users');
$table->dropColumn('created_by');
});
Schema::table('historiques', function(Blueprint $table) {
$table->dropForeign('FK_historiques_users');
$table->unsignedBigInteger('user_id')->nullable()->change();
$table->foreign('user_id')
->references('id')
->on('users')
->nullOnDelete();
});
// Update 'historiques' descriptions with user datas.
DB::transaction(function () {
DB::statement(
"UPDATE historiques h
JOIN users u ON u.id = h.user_id
SET h.description = CONCAT(
IFNULL(h.description, ''),
' - ',
u.firstname, ' ',
UPPER(u.lastname),
' (', u.matricule, ')'
);"
);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('media', function (Blueprint $table) {
$table->foreignId('created_by')->nullable()->after('commentaire');
$table->foreign('created_by', 'FK_medias_users')->on('users')->references('id');
});
Schema::table('media', function (Blueprint $table) {
$table->dropForeign('historiques_user_id_foreign');
$table->unsignedBigInteger('user_id')->nullable()->change();
$table->foreign('user_id', 'FK_historiques_users')
->references('id')
->on('users')
->cascadeOnDelete();
});
}
};

View file

@ -0,0 +1,51 @@
<?php
use App\Models\ArcadiaNatureFichier;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('arcadia_nature_fichiers', function (Blueprint $table) {
$table->id();
$table->string('value');
$table->string('label');
$table->timestamps();
});
ArcadiaNatureFichier::query()->create([
'value' => 'Undefined',
'label' => 'Non défini'
]);
ArcadiaNatureFichier::query()->create([
'value' => 'A',
'label' => 'Photographie d\'un produit en definition standard'
]);
ArcadiaNatureFichier::query()->create([
'value' => 'C',
'label' => 'Photographie d\'un produit en Haute Definition'
]);
ArcadiaNatureFichier::query()->create([
'value' => 'H',
'label' => 'Mobile Ready Hero Image = autre (dessin, illustration, logotype, pictogramme)'
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('arcadia_nature_fichiers');
}
};

View file

@ -0,0 +1,55 @@
<?php
use App\Models\ArcadiaAnglePriseVue;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('arcadia_angle_prise_vues', function (Blueprint $table) {
$table->id();
$table->string('value');
$table->string('label');
$table->timestamps();
});
ArcadiaAnglePriseVue::query()->create([
'value' => 'Undefined',
'label' => 'Non défini'
]);
ArcadiaAnglePriseVue::query()->create([
'value' => 'C',
'label' => 'Centre, angle de plongee 15 degres'
]);
ArcadiaAnglePriseVue::query()->create([
'value' => 'L',
'label' => '3/4 gauche'
]);
ArcadiaAnglePriseVue::query()->create([
'value' => 'N',
'label' => 'Centre, sans angle de plongee'
]);
ArcadiaAnglePriseVue::query()->create([
'value' => 'R',
'label' => '3/4 droit'
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('arcadia_angle_prise_vues');
}
};

View file

@ -0,0 +1,55 @@
<?php
use App\Models\ArcadiaTypeContenu;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('arcadia_type_contenus', function (Blueprint $table) {
$table->id();
$table->string('value');
$table->string('label');
$table->timestamps();
});
ArcadiaTypeContenu::query()->create([
'value' => 'Undefined',
'label' => 'Non défini'
]);
ArcadiaTypeContenu::query()->create([
'value' => 'D',
'label' => 'Produit prepare (monte, pour les produits a assembler)'
]);
ArcadiaTypeContenu::query()->create([
'value' => 'G',
'label' => 'Mise en situation (produits emballes ou deballes, suggestion de presentation)'
]);
ArcadiaTypeContenu::query()->create([
'value' => '0',
'label' => 'Produit nu/deballe'
]);
ArcadiaTypeContenu::query()->create([
'value' => '1',
'label' => 'produit emballe/packshot'
]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('arcadia_type_contenus');
}
};

View file

@ -0,0 +1,55 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('media', function (Blueprint $table) {
$table->unsignedBigInteger('arcadia_angle_prise_vue_id')
->after('variante_id')
->default(1);
$table->foreign('arcadia_angle_prise_vue_id')
->references('id')
->on('arcadia_angle_prise_vues');
$table->unsignedBigInteger('arcadia_nature_fichier_id')
->after('arcadia_angle_prise_vue_id')
->default(1);
$table->foreign('arcadia_nature_fichier_id')
->references('id')
->on('arcadia_nature_fichiers');
$table->unsignedBigInteger('arcadia_type_contenu_id')
->after('arcadia_nature_fichier_id')
->default(1);
$table->foreign('arcadia_type_contenu_id')
->references('id')
->on('arcadia_type_contenus');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('media', function (Blueprint $table) {
$table->dropColumn([
'arcadia_angle_prise_vue_id',
'arcadia_nature_fichier_id',
'arcadia_type_contenu_id',
]);
});
}
};

View file

@ -0,0 +1,30 @@
<?php
use App\Enums\Users\RoleEnum;
use App\Models\User;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
User::query()->update(['role' => RoleEnum::reader]);
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
// No possibility to revert this migration.
}
};

104
edl.md Normal file
View file

@ -0,0 +1,104 @@
# DAM
## Type vues
| Clé DAM | Valeur | Clé Arcadia |
| --- | ------ | ----------- |
| 1 | Facing | 1 |
| 2 | Côté gauche | 2 |
| 3 | Dessus | 3 |
| 4 | Ambiance | xxx |
| 5 | 3D | xxx |
| 6 | Produit nu | xxx |
| 7 | Dos | 7 |
| 8 | Côté droit | 8 |
| 9 | Dessous | 9 |
| 10 | Standard | 10 |
| 11 | Logo société | 11 |
| 12 | Logo activité | 12 |
| 13 | Logo | 13 |
| 14 | Logo organisme certificateur | 14 |
| 15 | Logos signe de qualité | 15 |
| 16 | Logo standard | 16 |
| 17 | Etiquette Colis | 17 |
| 18 | CB Colis | 18 |
| 19 | BAT | xxx |
| 20 | Non applicable (vue interne / zoom / vue en coupe) | 0 |
| 21 | Charte Graphique | 21 |
| 22 | Portrait | 22 |
| 23 | Paysage | 23 |
| 24 | Panoramique | 24 |
| xx | En-tête | 25 |
| xx | Pied-de-page | 26 |
| 27 | En-tête | 27 |
| 28 | Pied-de-page | 28 |
## Type Media
| Clé DAM | Valeur | Clé Arcadia |
| --- | ------ | ----------- |
| 1 | Photo | 1 |
| 2 | Illustration/Vectorielle | 2 |
| 3 | Vidéo | 3 |
| 4 | Audio | 4 |
| 5 | jpeg | 5 |
| 6 | BAT | 6 |
## Variantes
| Clé DAM | Valeur | Clé Arcadia |
| --- | ------ | ----------- |
| 1 | Permanent | 1 |
| 2 | Eté | 2 |
| 3 | Noël | 3 |
| 4 | Saveurs de l'année | 4 |
| 5 | Promo | 5 |
| 6 | Carrefour | 6 |
| 7 | Permanent ambiance | 7 |
| 8 | permanent 3D | 8 |
| 9 | SDA 2018 | 9 |
| 10 | Permanent dos | 10 |
| 11 | Standard document | 11 |
| 12 | Std document | 12 |
| 13 | std | 13 |
| 14 | été ambiance | 14 |
| 15 | PERMANENT | 15 |
| 16 | HIVER | 16 |
| 17 | Standard | 17 |
| 18 | BAT | 18 |
| 19 | Signe de qualité | 19 |
| 20 | Standard Panoramique | 20 |
## Sujet
| Clé DAM | Valeur | Clé Arcadia |
| --- | ------ | ----------- |
| 1 | Visuel Article | 1 |
| 2 | Marque | 2 |
| 3 | Pièces Anatomiques - Sous familles de ventes | 3 |
| 4 | Sociétés | 4 |
| 5 | Logos signe de qualité | 5 |
| 6 | Organisme Certificateur | 6 |
| 7 | Groupement Qualité | 7 |
| 8 | Logo Allégation Marketing | 8 |
| 9 | CB Colis | 9 |
| 10 | Etiquette Colis | 10 |
| 11 | Groupe | 11 |
| xx | Etiquette Piece | 12 |
| xx | Rouleau | 13 |
## Langues
| Clé DAM | Valeur | Clé Arcadia |
| --- | ------ | ----------- |
| 1 | Francais | 1 |
| 2 | English | 2 |
| 3 | Español | 3 |
| 4 | Polonais | 4 |
| 5 | Allemand | 5 |
| 6 | Italien | 6 |
| xx | Hongrois | 7 |
| 11 | Danois | 11 |
| 13 | Neerlandais | 13 |

12
lang/fr/enums.php Normal file
View file

@ -0,0 +1,12 @@
<?php
return [
'user-role' => [
'conceptor' => 'Concepteur',
'admin' => 'Administrateur',
'contributorarcadia' => 'Contributeur Arcadia',
'contributor' => 'Contributeur',
'contributorlimited' => 'Contributeur Restreint',
'reader' => 'Lecteur',
],
];

View file

@ -25,6 +25,13 @@ $(document).ready(function() {
tableGlobalSearch('.datatable tr.data_row', search, [1,2,3]);
});
// ---- Gestion des utilisateurs : recherche de marque
$(".filter_brand_btn").on('click', function(event) {
event.preventDefault();
const search = $(".filter_brand_input").val();
tableGlobalSearch('.datatable tr.data_row', search, [1,2,3]);
});
// ---- Gestion des dossiers : recherche un dossier
$(".filter_dossier_btn").on('click', function(event) {
event.preventDefault();
@ -41,20 +48,10 @@ $(document).ready(function() {
// ---- Edition des utilisateurs : suppression d'utilisateur
$(".btn_delete_user").on('click', function(event) {
event.preventDefault();
let form = $('.deleteform'), created_by = $('select[name="created_by"]').val(),
created_by_text = $('select[name="created_by"] option:selected').text()
if (!created_by.length) {
$.fancyConfirm({
title: 'Impossible de lancer cet action.',
message: 'Vous devez choisir autre un utilisateur à qui assigner les fiches.',
okButton: 'Compris',
noButton: 'Retour'
})
return;
}
let form = $('.deleteform');
$.fancyConfirm({
title: 'Suppression d\'un utilisateur',
message: 'Les média seront assignés à ' + created_by_text + '. Êtes-vous sûr de vouloir supprimer cet utilisateur ?',
message: 'Êtes-vous sûr de vouloir supprimer cet utilisateur ?',
okButton: 'Supprimer',
noButton: 'Annuler',
callback: function (value) {
@ -195,6 +192,7 @@ $(document).ready(function() {
initSelector(".selector_marques");
initSelector(".selector_sujets");
initSelector(".selector_users");
});

View file

@ -1,7 +1,7 @@
<template>
<div>
<button
v-if="storeRole !== 2"
v-if="isAtLeastRole(storeRole, RoleEnum.contributorlimited)"
v-on:click.prevent="openUploadFrom"
class="action_add_media"
>
@ -13,7 +13,7 @@
<span> Télécharger</span>
</button>
<button
v-if="(storeRole === 1 || storeRole === 4) && !selectionContainsLockedMedias"
v-if="isAtLeastRole(storeRole, RoleEnum.contributor) && !selectionContainsLockedMedias"
v-on:click.prevent="deleteMedia"
class="action_delete_media"
>
@ -21,7 +21,7 @@
<span> Supprimer</span>
</button>
<button
v-if="storeRole !== 2 && !selectionContainsLockedMedias"
v-if="isAtLeastRole(storeRole, RoleEnum.contributorlimited) && !selectionContainsLockedMedias"
v-on:click.prevent="duplicateMedia"
class="action_duplicate_media"
>
@ -29,7 +29,7 @@
<span> Dupliquer</span>
</button>
<button
v-if="storeRole !== 2 && !selectionContainsLockedMedias"
v-if="isAtLeastRole(storeRole, RoleEnum.contributorlimited) && !selectionContainsLockedMedias"
v-on:click.prevent="applySticker"
class="action_tag_media"
>
@ -165,6 +165,7 @@
<script>
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import Resumable from "resumablejs";
import { isAtLeastRole, RoleEnum } from "../../role-enum";
export default {
name: "MmActionsComponent",
@ -205,6 +206,8 @@ export default {
format_id: null,
waitingRequest: false,
resumable: null,
RoleEnum,
isAtLeastRole,
};
},
methods: {

View file

@ -19,39 +19,41 @@
<span>Aperçu</span>
</button>
<div v-show="this.storeMedia.dossier_id !== 1">
<button
v-on:click.prevent="currentTab = 'edit'"
:class="
currentTab === 'edit'
? 'tabbtn form_media_edit active'
: 'tabbtn form_media_edit'
"
data-tab="1"
>
<span>Edition</span>
</button>
<button
v-on:click.prevent="currentTab = 'links'"
:class="
currentTab === 'links'
<div v-if="isAtLeastRole(storeRole, RoleEnum.contributorlimited)">
<button
v-on:click.prevent="currentTab = 'edit'"
:class="
currentTab === 'edit'
? 'tabbtn form_media_edit active'
: 'tabbtn form_media_edit'
"
data-tab="1"
>
<span>Edition</span>
</button>
<button
v-on:click.prevent="currentTab = 'links'"
:class="
currentTab === 'links'
? 'tabbtn form_media_links active'
: 'tabbtn form_media_links'
"
data-tab="2"
>
<span>Vignettes</span>
</button>
<button
v-on:click.prevent="currentTab = 'history'"
:class="
currentTab === 'history'
"
data-tab="2"
>
<span>Vignettes</span>
</button>
<button
v-on:click.prevent="currentTab = 'history'"
:class="
currentTab === 'history'
? 'tabbtn form_media_history active'
: 'tabbtn form_media_history'
"
data-tab="3"
>
<span>Historique</span>
</button>
"
data-tab="3"
>
<span>Historique</span>
</button>
</div>
<button v-on:click.prevent="close" class="form_media_close">
<font-awesome-icon icon="times" />
@ -89,6 +91,10 @@
:marques="marques"
:etiquettes="etiquettes"
:copyrights="copyrights"
:naturesfichier="naturesfichier"
:typescontenu="typescontenu"
:anglesprisevue="anglesprisevue"
:tags="tags"
@closeEdit="close"
/>
<MeLinksComponent :currentTab="currentTab" />
@ -106,6 +112,7 @@ import MeEditComponent from "./tabs/MeEditComponent.vue";
import MeHistoryComponent from "./tabs/MeHistoryComponent.vue";
import MeLinksComponent from "./tabs/MeLinksComponent.vue";
import MePreviewComponent from "./tabs/MePreviewComponent.vue";
import { isAtLeastRole, RoleEnum } from "../../../role-enum.js";
export default {
name: "MmEditComponent",
@ -125,6 +132,10 @@ export default {
this.marques = data.marques;
this.etiquettes = data.etiquettes;
this.copyrights = data.copyrights;
this.naturesfichier = data.naturesfichier;
this.typescontenu = data.typescontenu;
this.anglesprisevue = data.anglesprisevue;
this.tags = data.tags;
if (this.open !== undefined) {
this.$store.commit({
type: "updateMedia",
@ -163,6 +174,12 @@ export default {
marques: [],
etiquettes: [],
copyrights: [],
naturesfichier: [],
typescontenu: [],
anglesprisevue: [],
tags: [],
RoleEnum,
isAtLeastRole,
};
},
watch: {

View file

@ -10,7 +10,7 @@
>
<div
v-if="
storeRole === 1 &&
isAtLeastRole(storeRole, RoleEnum.contributorarcadia) &&
(this.oMedia.sujet_id === 1 ||
this.oMedia.sujet_id === 2 ||
this.oMedia.sujet_id === 4)
@ -41,17 +41,6 @@
>
<label>Date de création : {{ this.oMedia.created_at }}</label>
<label>Date de modification : {{ this.oMedia.updated_at }}</label>
<label v-if="this.oMedia.owner !== null"
>Créé par :
{{
this.oMedia.owner.firstname +
" " +
this.oMedia.owner.lastname +
" (" +
this.oMedia.owner.matricule +
")"
}}</label
>
</div>
<div class="row">
<label>Intitulé <span>*</span></label>
@ -98,6 +87,66 @@
</option>
</select>
</div>
<div
class="row"
v-if="this.oMedia.sujet_id === 1"
>
<label>Nature du fichier <span>*</span></label>
<select
id="naturefichier"
name="naturefichier"
required
v-model="oMedia.arcadia_nature_fichier_id"
:disabled="oMedia.isLocked || oMedia.arcadia_id !== null"
>
<option :value="null" selected disabled>
Sélectionnez une nature de fichier
</option>
<option v-for="item in naturesfichier" :value="item.id">
{{ item.label }}
</option>
</select>
</div>
<div
class="row"
v-if="this.oMedia.sujet_id === 1"
>
<label>Type de contenu <span>*</span></label>
<select
id="typecontenu"
name="typecontenu"
required
v-model="oMedia.arcadia_type_contenu_id"
:disabled="oMedia.isLocked || oMedia.arcadia_id !== null"
>
<option :value="null" selected disabled>
Sélectionnez un type de contenu
</option>
<option v-for="item in typescontenu" :value="item.id">
{{ item.label }}
</option>
</select>
</div>
<div
class="row"
v-if="this.oMedia.sujet_id === 1"
>
<label>Angle prise de vue <span>*</span></label>
<select
id="angleprisevue"
name="angleprisevue"
required
v-model="oMedia.arcadia_angle_prise_vue_id"
:disabled="oMedia.isLocked || oMedia.arcadia_id !== null"
>
<option :value="null" selected disabled>
Sélectionnez un type de contenu
</option>
<option v-for="item in anglesprisevue" :value="item.id">
{{ item.label }}
</option>
</select>
</div>
<div class="row">
<label>Langue <span>*</span></label>
<select
@ -308,7 +357,7 @@
<div class="row">
<label>Nuage de tags</label>
<v-select
:options="[]"
:options="tags"
label="libelle"
:create-option="(item) => ({ id: new Date().getTime(), libelle: item })"
v-model="oMedia.tags"
@ -350,7 +399,7 @@
</div>
<div class="button_bar">
<button
v-if="(this.storeRole === 1 || this.storeRole === 3) && !this.oMedia.deleted_at"
v-if="isAtLeastRole(storeRole, RoleEnum.contributor) && !this.oMedia.deleted_at"
class="btn btn-red btn_tab_edit_delete"
v-on:click.prevent="DeleteMedia"
type="button"
@ -361,7 +410,7 @@
</button>
<button
v-on:click.prevent="CheckForm"
v-if="storeRole !== 2"
v-if="isAtLeastRole(storeRole, RoleEnum.contributorlimited)"
class="btn btn_tab_edit_save"
type="button"
name="button"
@ -376,11 +425,23 @@
<script>
import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome";
import { isAtLeastRole, RoleEnum } from "../../../../role-enum";
export default {
name: "MeEditComponent",
components: { FontAwesomeIcon },
props: ["currentTab", "langues", "societes", "marques", "etiquettes", "copyrights"],
props: [
"currentTab",
"langues",
"societes",
"marques",
"etiquettes",
"copyrights",
"naturesfichier",
"typescontenu",
"anglesprisevue",
"tags",
],
computed: {
storeMedia() {
return this.$store.state.media;
@ -441,6 +502,8 @@ export default {
selectedSocietes: [],
searchMarques: "",
searchSocietes: "",
RoleEnum,
isAtLeastRole,
};
},
methods: {

32
resources/js/role-enum.js Normal file
View file

@ -0,0 +1,32 @@
// Roles from app/Enums/Users/RoleEnum.php
export const RoleEnum = {
conceptor: {
value: 1,
label: 'Concepteur',
},
admin: {
value: 2,
label: 'Administrateur',
},
contributorarcadia: {
value: 3,
label: 'Contributeur Arcadia',
},
contributor: {
value: 4,
label: 'Contributeur',
},
contributorlimited: {
value: 5,
label: 'Contributeur Restreint',
},
reader: {
value: 6,
label: 'Lecteur',
},
};
export function isAtLeastRole(userRole, expectedRole) {
return userRole <= expectedRole.value;
};

View file

@ -0,0 +1,41 @@
@extends('layouts.app')
@include('layouts.header')
@section('content')
<main class="user_index template_2col">
@include('modules.param-sideMenu')
<div class="main_content">
@include('modules.flashMessage')
<div class="heading">
<h1>{{ __('Edition des utilisateurs de la marque') }} : {{ $brand->libMarq }}</h1>
</div>
{!! Form::model($brand, ['route' => ['brands.updateUsers', $brand], 'enctype' => 'multipart/form-data', 'class' => 'dataform']) !!}
@method('PUT')
<div class="form-section">
{!! Form::label('users', 'Utilisateurs associés') !!}
</div>
<div class="form-field">
{!! Form::label("", "") !!}
<div class="selector_form selector_users">
{!! Form::text('search', null, ['class' => "selector_filter", 'placeholder' => "Rechercher...", "autocomplete" => "off"]) !!}
<div class="selector_list">
<ul class="selector_choices" >
@foreach ($userModels as $userModel)
<li data-value="{{$userModel->id}}" >{{$userModel->firstname}} {{$userModel->lastname}}</li>
@endforeach
</ul>
<ul class="selector_values" >
@foreach ($brand->users as $user)
<li data-value="{{$user->id}}" >{{$user->firstname}} {{$user->lastname}}</li>
@endforeach
</ul>
<input class="selector_dataValues" type="hidden" name="users" value="">
</div>
</div>
</div>
<div class="button-row">
{!! Form::submit('Enregistrer', [ 'class' => 'btn']) !!} <a href="{{ route('brands.index') }}" class="btn btn-black ml-10">Retour</a>
</div>
{!! Form::close() !!}
</div>
</main>
@endsection

View file

@ -0,0 +1,48 @@
@extends('layouts.app')
@include('layouts.header')
@section('content')
<main class="brand_index template_2col">
@include('modules.param-sideMenu')
<div class="main_content">
@include('modules.flashMessage')
<div class="heading">
<h1>{{ __('Gestion des marques') }}</h1>
</div>
<div class="filter_row group">
<div class="right_col">
<input type="text" value="" class="filter_brand_input">
<a href="#" class="btn btn-black filter_brand_btn">Rechercher une marque</a>
</div>
</div>
<table class="datatable">
<thead>
<tr>
<th>CODE</th>
<th>LIBELLE</th>
<th>ACTIONS</th>
</tr>
</thead>
@foreach ($brands as $brand)
<tr class="data_row">
<td>{{ $brand->codMarq }}</td>
<td>{{ $brand->libMarq }}</td>
<td>
<a href="{{ route('brands.editUsers', $brand) }}" class="btn" title="{{ __('Gérer les relations avec les utilisateurs') }}">
{{ __('Gérer les utilisateurs') }}
<i class="fas fa-users-cog"></i>
</a>
</td>
</tr>
@endforeach
<tfoot>
<tr>
<td colspan="4">
</td>
</tr>
</tfoot>
</table>
</div>
</main>
@endsection

View file

@ -19,9 +19,9 @@
<a href="{{ route('user.show', Auth::user()) }}" >Mes informations</a>
<a class="contact_btn" data-subjectId="1" href="javascript:;" >Signaler un problème</a>
<a class="contact_btn" data-subjectId="2" href="javascript:;" >Suggérer une amélioration</a>
@if (Auth::user()->hasRole(config('roles.const.ADMIN')))
@can('viewAny', \App\Models\User::class)
<a href="{{ route('user.index') }}" >Paramétrage</a>
@endif
@endcan
<a href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">Déconnexion</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
@csrf

View file

@ -7,6 +7,10 @@
'marques' => $marques,
'etiquettes' => $etiquettes,
'copyrights' => $copyrights,
'naturesfichier' => $naturesfichier,
'typescontenu' => $typescontenu,
'anglesprisevue' => $anglesprisevue,
'tags' => $tags,
];
if(session('autoOpen')) {
$dataVue['open'] = session('autoOpen');

View file

@ -1,18 +1,32 @@
<div class="side_menu">
<a @if(request()->routeIs('user.*')) class="active" @endif href="{{ route('user.index') }}">
<span>{{ __('Gestion des utilisateur') }}s</span>
<i class="fas fa-users-cog"></i>
</a>
<a @if(request()->routeIs('dossier.*')) class="active" @endif href="{{ route('dossier.index') }}">
<span>{{ __('Gestion des dossiers') }}</span>
<i class="fa-solid fa-folder"></i>
</a>
<a @if(request()->routeIs('recovery.*')) class="active" @endif href="{{ route('recovery.index') }}">
<span>{{ __('Récupération des médias') }}</span>
<i class="fas fa-undo-alt"></i>
</a>
<a @if(request()->routeIs('arcadialinks.*')) class="active" @endif href="{{ route('arcadialinks.index') }}">
<span>{{ __('Anomalies du lien Arcadia') }}</span>
<i class="fas fa-bug"></i>
</a>
@can('viewAny', \App\Models\User::class)
<a @if(request()->routeIs('user.*')) class="active" @endif href="{{ route('user.index') }}">
<span>{{ __('Gestion des utilisateur') }}s</span>
<i class="fas fa-users-cog"></i>
</a>
@endcan
@can('viewAny', \App\Models\Marque::class)
<a @if(request()->routeIs('brands.*')) class="active" @endif href="{{ route('brands.index') }}">
<span>{{ __('Gestion des marques') }}</span>
<i class="fas fa-building"></i>
</a>
@endcan
@can('viewAny', \App\Models\Dossier::class)
<a @if(request()->routeIs('dossier.*')) class="active" @endif href="{{ route('dossier.index') }}">
<span>{{ __('Gestion des dossiers') }}</span>
<i class="fa-solid fa-folder"></i>
</a>
@endcan
@can('recover', \App\Models\Media::class)
<a @if(request()->routeIs('recovery.*')) class="active" @endif href="{{ route('recovery.index') }}">
<span>{{ __('Récupération des médias') }}</span>
<i class="fas fa-undo-alt"></i>
</a>
@endcan
@can('viewAny', \App\Models\ArcadiaLink::class)
<a @if(request()->routeIs('arcadialinks.*')) class="active" @endif href="{{ route('arcadialinks.index') }}">
<span>{{ __('Anomalies du lien Arcadia') }}</span>
<i class="fas fa-bug"></i>
</a>
@endcan
</div>

View file

@ -28,7 +28,11 @@
</div>
<div class="form-field">
{!! Form::label('role', 'Rôle') !!}
{!! Form::select('role', $roles) !!}
<select name="role">
@foreach(\App\Enums\Users\RoleEnum::getFormCases() as $role)
<option value="{{ $role->value }}">{{ $role->label() }}</option>
@endforeach
</select>
</div>
<div class="form-section">
{!! Form::label('marques', 'Marques associées') !!}

View file

@ -32,8 +32,13 @@
<div class="form-field">
{!! Form::label("", "") !!}
<select id="role" name="role" required>
@foreach($roles as $value => $label)
<option value="{{ $value }}" @if(old('role', optional($user)->role) === $value)selected @endif>{{ $label }}</option>
@foreach(\App\Enums\Users\RoleEnum::getFormCases() as $role)
<option
value="{{ $role->value }}"
@selected(old('role', optional($user)->role->value) === $role->value)
>
{{ $role->label() }}
</option>
@endforeach
</select>
</div>
@ -89,12 +94,6 @@
<a href="{{ route('user.index') }}" class="btn btn-black ml-10">Annuler</a>
{!! Form::model($user, ['route' => ['user.destroy', $user], 'enctype' => 'multipart/form-data', 'class' => 'deleteform floatr']) !!}
@method('DELETE')
<select class="custom-select" name="created_by" required>
<option value="" selected>{{ __('Sélectionnez un utilisateur à qui assigner les fiches') }}</option>
@foreach($users as $user)
<option value="{{ $user->id }}">{{ $user->email }}</option>
@endforeach
</select>
<button class="btn btn-black btn_delete_user">{{ __('Supprimer cet utilisateur') }}</button>
{!! Form::close() !!}
</div>

View file

@ -14,8 +14,8 @@
<div class="left_col">
<select class="filter_role" >
<option value="0">Filtrer par rôle</option>
@foreach (config('roles.label') as $key => $oneRole)
<option value="{{$key}}">{{$oneRole}}</option>
@foreach (\App\Enums\Users\RoleEnum::cases() as $role)
<option value="{{ $role->value }}">{{ $role->label() }}</option>
@endforeach
</select>
<a href="#" class=" btn btn-black filter_role_btn">Ok</a>
@ -40,7 +40,7 @@
<td><a href="{{ route('user.edit', $user) }}">{{ $user->matricule }}</a></td>
<td>{{ $user->firstname." ".$user->lastname }}</td>
<td>{{ $user->email }}</td>
<td>{{ $user->getRoleLabel()}}</td>
<td>{{ $user->role->label()}}</td>
</tr>
@endforeach
<tfoot>

View file

@ -34,7 +34,7 @@
</div>
<div class="form-field">
{!! Form::label("", "") !!}
{!! Form::text('role', $user->getRoleLabel(), ['disabled']) !!}
{!! Form::text('role', $user->role->label(), ['disabled']) !!}
</div>
<div class="form-section">
{!! Form::label('marques', 'Marques associées') !!}

View file

@ -2,6 +2,7 @@
use App\Http\Controllers\ArcadiaLinkController;
use App\Http\Controllers\ArticleController;
use App\Http\Controllers\BrandController;
use App\Http\Controllers\CommonController;
use App\Http\Controllers\DossierController;
use App\Http\Controllers\MediaController;
@ -57,6 +58,9 @@ Route::group(['middleware' => ['auth']], function () {
// * Back office .
Route::resource('user', UserController::class);
Route::get('/brands', [BrandController::class, 'index'])->name('brands.index');
Route::get('/brands/{brand}/edit-users', [BrandController::class, 'editUsers'])->name('brands.editUsers');
Route::put('/brands/{brand}/edit-users', [BrandController::class, 'updateUsers'])->name('brands.updateUsers');
Route::resource('arcadialinks', ArcadiaLinkController::class)->only(['index', 'destroy']);
Route::resource('dossier', DossierController::class)->except('show');
Route::get('/recovery', [RecoveryController::class, 'index'])->name('recovery.index');