removed S3 and handles bugs that came with it
This commit is contained in:
parent
3d23e9cec3
commit
59bc78fe47
|
|
@ -88,24 +88,27 @@ export default class extends Controller {
|
|||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const data = cell.getRow().getData();
|
||||
const url = cell.getValue();
|
||||
let url = cell.getValue(); // use 'let' so we can modify it
|
||||
|
||||
// 1. GENERATE INITIALS
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${first(data.name)}${first(data.prenom)}`;
|
||||
|
||||
// wrapper is for centering and circle clipping
|
||||
// 2. CREATE WRAPPER
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "avatar-wrapper";
|
||||
// same size for both cases
|
||||
wrapper.style.width = "40px";
|
||||
wrapper.style.height = "40px";
|
||||
wrapper.style.display = "flex";
|
||||
wrapper.style.alignItems = "center";
|
||||
wrapper.style.justifyContent = "center";
|
||||
wrapper.style.borderRadius = "50%";
|
||||
wrapper.style.overflow = "hidden"; // ensure image clips to circle
|
||||
wrapper.style.overflow = "hidden";
|
||||
|
||||
if (!url) {
|
||||
wrapper.style.background = "#6c757d"; // gray background
|
||||
// Helper to render fallback initials
|
||||
const renderFallback = () => {
|
||||
wrapper.innerHTML = ""; // clear any broken img
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
|
|
@ -113,31 +116,36 @@ export default class extends Controller {
|
|||
span.style.fontSize = "14px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
};
|
||||
|
||||
// 3. IF NO URL, RENDER FALLBACK IMMEDIATELY
|
||||
if (!url) {
|
||||
renderFallback();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Image case: make it fill the same wrapper
|
||||
// --- THE FIX: HANDLE RELATIVE PATHS ---
|
||||
// If the path doesn't start with 'http' or '/', add a leading '/'
|
||||
// This ensures 'uploads/file.jpg' becomes '/uploads/file.jpg'
|
||||
if (!url.startsWith('http') && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
// 4. CREATE IMAGE
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = initials || "avatar";
|
||||
img.style.width = "100%";
|
||||
img.style.height = "100%";
|
||||
img.style.objectFit = "cover"; // keep aspect and cover circle
|
||||
img.style.objectFit = "cover";
|
||||
|
||||
// 5. ERROR HANDLING (triggers your fallback)
|
||||
img.onerror = () => {
|
||||
console.warn("Image failed to load:", url); // Debug log to see the wrong path
|
||||
renderFallback();
|
||||
};
|
||||
|
||||
wrapper.appendChild(img);
|
||||
|
||||
// Optional: fallback if image fails
|
||||
img.addEventListener("error", () => {
|
||||
wrapper.innerHTML = "";
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
span.style.fontWeight = "600";
|
||||
span.style.fontSize = "12px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
},
|
||||
|
|
@ -305,23 +313,27 @@ export default class extends Controller {
|
|||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const data = cell.getRow().getData();
|
||||
const url = cell.getValue();
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${data.initials}`;
|
||||
let url = cell.getValue(); // use 'let' so we can modify it
|
||||
|
||||
// 1. GENERATE INITIALS
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${first(data.name)}${first(data.prenom)}`;
|
||||
|
||||
// 2. CREATE WRAPPER
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "avatar-wrapper";
|
||||
// same size for both cases
|
||||
wrapper.style.width = "40px";
|
||||
wrapper.style.height = "40px";
|
||||
wrapper.style.display = "flex";
|
||||
wrapper.style.alignItems = "center";
|
||||
wrapper.style.justifyContent = "center";
|
||||
wrapper.style.borderRadius = "50%";
|
||||
wrapper.style.overflow = "hidden"; // ensure image clips to circle
|
||||
wrapper.style.overflow = "hidden";
|
||||
|
||||
if (!url) {
|
||||
wrapper.style.background = "#6c757d"; // gray background
|
||||
// Helper to render fallback initials
|
||||
const renderFallback = () => {
|
||||
wrapper.innerHTML = ""; // clear any broken img
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
|
|
@ -329,31 +341,36 @@ export default class extends Controller {
|
|||
span.style.fontSize = "14px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
};
|
||||
|
||||
// 3. IF NO URL, RENDER FALLBACK IMMEDIATELY
|
||||
if (!url) {
|
||||
renderFallback();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Image case: make it fill the same wrapper
|
||||
// --- THE FIX: HANDLE RELATIVE PATHS ---
|
||||
// If the path doesn't start with 'http' or '/', add a leading '/'
|
||||
// This ensures 'uploads/file.jpg' becomes '/uploads/file.jpg'
|
||||
if (!url.startsWith('http') && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
// 4. CREATE IMAGE
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = initials || "avatar";
|
||||
img.style.width = "100%";
|
||||
img.style.height = "100%";
|
||||
img.style.objectFit = "cover"; // keep aspect and cover circle
|
||||
img.style.objectFit = "cover";
|
||||
|
||||
// 5. ERROR HANDLING (triggers your fallback)
|
||||
img.onerror = () => {
|
||||
console.warn("Image failed to load:", url); // Debug log to see the wrong path
|
||||
renderFallback();
|
||||
};
|
||||
|
||||
wrapper.appendChild(img);
|
||||
|
||||
// Optional: fallback if image fails
|
||||
img.addEventListener("error", () => {
|
||||
wrapper.innerHTML = "";
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
span.style.fontWeight = "600";
|
||||
span.style.fontSize = "12px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
},
|
||||
|
|
@ -411,23 +428,27 @@ export default class extends Controller {
|
|||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const data = cell.getRow().getData();
|
||||
const url = cell.getValue();
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${data.initials}`;
|
||||
let url = cell.getValue(); // use 'let' so we can modify it
|
||||
|
||||
// 1. GENERATE INITIALS
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${first(data.name)}${first(data.prenom)}`;
|
||||
|
||||
// 2. CREATE WRAPPER
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "avatar-wrapper";
|
||||
// same size for both cases
|
||||
wrapper.style.width = "40px";
|
||||
wrapper.style.height = "40px";
|
||||
wrapper.style.display = "flex";
|
||||
wrapper.style.alignItems = "center";
|
||||
wrapper.style.justifyContent = "center";
|
||||
wrapper.style.borderRadius = "50%";
|
||||
wrapper.style.overflow = "hidden"; // ensure image clips to circle
|
||||
wrapper.style.overflow = "hidden";
|
||||
|
||||
if (!url) {
|
||||
wrapper.style.background = "#6c757d"; // gray background
|
||||
// Helper to render fallback initials
|
||||
const renderFallback = () => {
|
||||
wrapper.innerHTML = ""; // clear any broken img
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
|
|
@ -435,31 +456,36 @@ export default class extends Controller {
|
|||
span.style.fontSize = "14px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
};
|
||||
|
||||
// 3. IF NO URL, RENDER FALLBACK IMMEDIATELY
|
||||
if (!url) {
|
||||
renderFallback();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Image case: make it fill the same wrapper
|
||||
// --- THE FIX: HANDLE RELATIVE PATHS ---
|
||||
// If the path doesn't start with 'http' or '/', add a leading '/'
|
||||
// This ensures 'uploads/file.jpg' becomes '/uploads/file.jpg'
|
||||
if (!url.startsWith('http') && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
// 4. CREATE IMAGE
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = initials || "avatar";
|
||||
img.style.width = "100%";
|
||||
img.style.height = "100%";
|
||||
img.style.objectFit = "cover"; // keep aspect and cover circle
|
||||
img.style.objectFit = "cover";
|
||||
|
||||
// 5. ERROR HANDLING (triggers your fallback)
|
||||
img.onerror = () => {
|
||||
console.warn("Image failed to load:", url); // Debug log to see the wrong path
|
||||
renderFallback();
|
||||
};
|
||||
|
||||
wrapper.appendChild(img);
|
||||
|
||||
// Optional: fallback if image fails
|
||||
img.addEventListener("error", () => {
|
||||
wrapper.innerHTML = "";
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
span.style.fontWeight = "600";
|
||||
span.style.fontSize = "12px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
},
|
||||
|
|
@ -539,23 +565,27 @@ export default class extends Controller {
|
|||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const data = cell.getRow().getData();
|
||||
const url = cell.getValue();
|
||||
let url = cell.getValue(); // use 'let' so we can modify it
|
||||
|
||||
// 1. GENERATE INITIALS
|
||||
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
|
||||
const initials = `${first(data.name)}${first(data.prenom)}`;
|
||||
|
||||
// 2. CREATE WRAPPER
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "avatar-wrapper";
|
||||
// same size for both cases
|
||||
wrapper.style.width = "40px";
|
||||
wrapper.style.height = "40px";
|
||||
wrapper.style.display = "flex";
|
||||
wrapper.style.alignItems = "center";
|
||||
wrapper.style.justifyContent = "center";
|
||||
wrapper.style.borderRadius = "50%";
|
||||
wrapper.style.overflow = "hidden"; // ensure image clips to circle
|
||||
wrapper.style.overflow = "hidden";
|
||||
|
||||
if (!url) {
|
||||
wrapper.style.background = "#6c757d"; // gray background
|
||||
// Helper to render fallback initials
|
||||
const renderFallback = () => {
|
||||
wrapper.innerHTML = ""; // clear any broken img
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
|
|
@ -563,31 +593,36 @@ export default class extends Controller {
|
|||
span.style.fontSize = "14px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
};
|
||||
|
||||
// 3. IF NO URL, RENDER FALLBACK IMMEDIATELY
|
||||
if (!url) {
|
||||
renderFallback();
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// Image case: make it fill the same wrapper
|
||||
// --- THE FIX: HANDLE RELATIVE PATHS ---
|
||||
// If the path doesn't start with 'http' or '/', add a leading '/'
|
||||
// This ensures 'uploads/file.jpg' becomes '/uploads/file.jpg'
|
||||
if (!url.startsWith('http') && !url.startsWith('/')) {
|
||||
url = '/' + url;
|
||||
}
|
||||
|
||||
// 4. CREATE IMAGE
|
||||
const img = document.createElement("img");
|
||||
img.src = url;
|
||||
img.alt = initials || "avatar";
|
||||
img.style.width = "100%";
|
||||
img.style.height = "100%";
|
||||
img.style.objectFit = "cover"; // keep aspect and cover circle
|
||||
img.style.objectFit = "cover";
|
||||
|
||||
// 5. ERROR HANDLING (triggers your fallback)
|
||||
img.onerror = () => {
|
||||
console.warn("Image failed to load:", url); // Debug log to see the wrong path
|
||||
renderFallback();
|
||||
};
|
||||
|
||||
wrapper.appendChild(img);
|
||||
|
||||
// Optional: fallback if image fails
|
||||
img.addEventListener("error", () => {
|
||||
wrapper.innerHTML = "";
|
||||
wrapper.style.background = "#6c757d";
|
||||
const span = document.createElement("span");
|
||||
span.className = "avatar-initials";
|
||||
span.style.color = "#fff";
|
||||
span.style.fontWeight = "600";
|
||||
span.style.fontSize = "12px";
|
||||
span.textContent = initials || "•";
|
||||
wrapper.appendChild(span);
|
||||
});
|
||||
|
||||
return wrapper;
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
parameters:
|
||||
aws_url: '%env(AWS_ENDPOINT)%'
|
||||
aws_public_url: '%env(AWS_ENDPOINT)%'
|
||||
aws_bucket: '%env(S3_PORTAL_BUCKET)%'
|
||||
logos_directory: '%kernel.project_dir%/public/uploads/logos'
|
||||
|
||||
services:
|
||||
|
|
|
|||
|
|
@ -128,12 +128,15 @@ class UserController extends AbstractController
|
|||
'isActive' => true,
|
||||
]);
|
||||
if (!$uoList) {
|
||||
$this->loggerService->logEntityNotFound('UsersOrganization', [
|
||||
'user_id' => $user->getId(),
|
||||
'organization_id' => $orgId],
|
||||
$actingUser->getId());
|
||||
$this->addFlash('error', "L'utilisateur n'est pas actif dans une organisation.");
|
||||
throw $this->createNotFoundException(self::NOT_FOUND);
|
||||
$data['rolesArray'] = $this->userService->getRolesArrayForUser($actingUser, true);
|
||||
return $this->render('user/show.html.twig', [
|
||||
'user' => $user,
|
||||
'organizationId' => $orgId ?? null,
|
||||
'uoActive' => $uoActive ?? null,
|
||||
'apps' => $apps ?? [],
|
||||
'data' => $data ?? [],
|
||||
'canEdit' => false,
|
||||
]);
|
||||
}
|
||||
}
|
||||
// Charger les liens UserOrganizationApp (UOA) actifs pour les UO trouvées
|
||||
|
|
@ -168,7 +171,11 @@ class UserController extends AbstractController
|
|||
$canEdit = $this->userService->canEditRolesCheck($actingUser, $user,$this->isGranted('ROLE_ADMIN'), $singleUo, $organization);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$this->errorLogger->error($e->getMessage());
|
||||
$this->loggerService->logError('error while loading user information', [
|
||||
'target_user_id' => $id,
|
||||
'acting_user_id' => $actingUser->getId(),
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
$this->addFlash('error', 'Une erreur est survenue lors du chargement des informations utilisateur.');
|
||||
$referer = $request->headers->get('referer');
|
||||
return $this->redirect($referer ?? $this->generateUrl('app_index'));
|
||||
|
|
@ -199,13 +206,12 @@ class UserController extends AbstractController
|
|||
|
||||
$form = $this->createForm(UserForm::class, $user);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
|
||||
// Handle user edit
|
||||
$picture = $form->get('pictureUrl')->getData();
|
||||
$picture = $form->get('pictureUrl')->getData();;
|
||||
$this->userService->formatUserData($user, $picture);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
|
||||
$this->entityManager->persist($user);
|
||||
$this->entityManager->flush();
|
||||
|
||||
|
|
@ -571,6 +577,7 @@ class UserController extends AbstractController
|
|||
|
||||
$user->setIsActive(false);
|
||||
$user->setIsDeleted(true);
|
||||
$this->userService->deleteProfilePicture($user);
|
||||
$user->setModifiedAt(new \DateTimeImmutable('now'));
|
||||
// Deactivate all org links
|
||||
$this->userOrganizationService->deactivateAllUserOrganizationLinks($actingUser, $user);
|
||||
|
|
@ -731,10 +738,9 @@ class UserController extends AbstractController
|
|||
|
||||
// Map to array
|
||||
$data = array_map(function (User $user) {
|
||||
$picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl();
|
||||
return [
|
||||
'id' => $user->getId(),
|
||||
'pictureUrl' => $picture,
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'name' => $user->getSurname(),
|
||||
'prenom' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
|
|
@ -786,10 +792,9 @@ class UserController extends AbstractController
|
|||
// Map to array (keep isConnected)
|
||||
$data = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
$picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl();
|
||||
$initials = $user->getName()[0] . $user->getSurname()[0];
|
||||
return [
|
||||
'pictureUrl' => $picture,
|
||||
'pictureUrl' =>$user->getPictureUrl(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]),
|
||||
|
|
@ -829,10 +834,9 @@ class UserController extends AbstractController
|
|||
// Map to array (keep isConnected)
|
||||
$data = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
$picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl();
|
||||
$initials = $user->getName()[0] . $user->getSurname()[0];
|
||||
return [
|
||||
'pictureUrl' => $picture,
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]),
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use RuntimeException;
|
|||
use Symfony\Bundle\SecurityBundle\Security;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use App\Event\UserCreatedEvent;
|
||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
|
||||
|
||||
class UserService
|
||||
|
|
@ -173,28 +174,91 @@ class UserService
|
|||
return ['none' => $group];
|
||||
}
|
||||
|
||||
public function handleProfilePicture(User $user, $picture): void
|
||||
public function handleProfilePicture(User $user, UploadedFile $picture): void
|
||||
{
|
||||
// Get file extension
|
||||
$extension = $picture->guessExtension();
|
||||
// 1. Define the destination directory (adjust path as needed, e.g., 'public/uploads/profile_pictures')
|
||||
$destinationDir = 'uploads/profile_pictures';
|
||||
|
||||
// Create custom filename: userNameUserSurname_dmyHis
|
||||
$customFilename = $user->getName() . $user->getSurname() . '_' . date('dmyHis') . '.' . $extension;
|
||||
try {
|
||||
$this->awsService->PutDocObj($_ENV['S3_PORTAL_BUCKET'], $picture, $customFilename, $extension, 'profile/');
|
||||
$this->loggerService->logAWSAction(
|
||||
'Profile picture uploaded to S3', [
|
||||
'user_id' => $user->getId(),
|
||||
'filename' => $customFilename,
|
||||
]);
|
||||
$user->setPictureUrl('profile/' . $customFilename);
|
||||
} catch (FileException $e) {
|
||||
// Handle upload error
|
||||
throw new FileException('File upload failed: ' . $e->getMessage());
|
||||
// 2. Create the directory if it doesn't exist
|
||||
if (!file_exists($destinationDir)) {
|
||||
// 0755 is the standard permission (Owner: read/write/exec, Others: read/exec)
|
||||
if (!mkdir($destinationDir, 0755, true) && !is_dir($destinationDir)) {
|
||||
throw new \RuntimeException(sprintf('Directory "%s" was not created', $destinationDir));
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
// 3. Get extension and create a safe filename
|
||||
$extension = $picture->guessExtension();
|
||||
|
||||
// Sanitize the filename to remove special characters/spaces to prevent filesystem errors
|
||||
$safeName = preg_replace('/[^a-zA-Z0-9]/', '', $user->getName() . $user->getSurname());
|
||||
$customFilename = $safeName . '_' . date('dmyHis') . '.' . $extension;
|
||||
|
||||
try {
|
||||
// 4. Move the file to the destination directory
|
||||
// The move() method is standard in Symfony/Laravel UploadedFile objects
|
||||
$picture->move($destinationDir, $customFilename);
|
||||
|
||||
// 5. Update the user entity with the relative path
|
||||
// Ensure you store the path relative to your public folder usually
|
||||
$user->setPictureUrl($destinationDir . '/' . $customFilename);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 6. Log the critical error as requested
|
||||
$this->loggerService->logError('File upload failed',[
|
||||
'target_user_id' => $user->getId(),
|
||||
'message' => $e->getMessage(),
|
||||
'file_name' => $customFilename,
|
||||
]);
|
||||
|
||||
// Optional: Re-throw the exception if you want the controller/user to know the upload failed
|
||||
throw new FileException('File upload failed.');
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteProfilePicture(User $user): void
|
||||
{
|
||||
// 1. Get the current picture path from the user entity
|
||||
$currentPicturePath = $user->getPictureUrl();
|
||||
|
||||
// If the user doesn't have a picture, simply return (nothing to delete)
|
||||
if (!$currentPicturePath) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 2. Check if the file exists on the server before trying to delete
|
||||
// Note: Ensure $currentPicturePath is relative to the script execution or absolute.
|
||||
if (file_exists($currentPicturePath)) {
|
||||
|
||||
// 3. Delete the file
|
||||
if (!unlink($currentPicturePath)) {
|
||||
throw new \Exception(sprintf('Could not unlink "%s"', $currentPicturePath));
|
||||
}
|
||||
} else {
|
||||
// Optional: Log a warning if the DB had a path but the file was missing
|
||||
$this->loggerService->logError('File not found on disk during deletion request', [
|
||||
'target_user_id' => $user->getId(),
|
||||
'missing_path' => $currentPicturePath
|
||||
]);
|
||||
}
|
||||
|
||||
// 4. Update the user entity to remove the reference
|
||||
$user->setPictureUrl("");
|
||||
|
||||
} catch (\Exception $e) {
|
||||
// 5. Log the critical error
|
||||
// We log it, but strictly speaking, we might still want to nullify the DB
|
||||
// if the file is corrupted/un-deletable to prevent broken images on the frontend.
|
||||
$this->loggerService->logError('File deletion failed', [
|
||||
'target_user_id' => $user->getId(),
|
||||
'message' => $e->getMessage(),
|
||||
'file_path' => $currentPicturePath,
|
||||
]);
|
||||
|
||||
// Re-throw if you want to stop execution/show error to user
|
||||
throw new \RuntimeException('Unable to remove profile picture.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -297,7 +361,7 @@ class UserService
|
|||
{
|
||||
$formatted = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
$picture = $this->awsService->getPublicUrl($_ENV['S3_PORTAL_BUCKET']) . $user->getPictureUrl();
|
||||
|
||||
if ($uo->getStatut() === "INVITED") {
|
||||
$statut = "INVITED";
|
||||
// if user invited but not accepted in 1 hour, set statut to EXPIRED
|
||||
|
|
@ -311,7 +375,7 @@ class UserService
|
|||
$statut = $uo->isActive() ? "ACTIVE" : "INACTIVE";
|
||||
}
|
||||
return [
|
||||
'pictureUrl' => $picture,
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'name' => $user->getSurname(),
|
||||
'prenom' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<div class="card-body">
|
||||
|
||||
{{ form_start(form, {'action': path('user_edit', {'id': user.id}), 'method': 'PUT'}) }}
|
||||
{{ form_start(form, {'action': path('user_edit', {'id': user.id}), 'method': 'POST'}) }}
|
||||
{{ form_widget(form) }}
|
||||
<input hidden type="text" value="{{ organizationId }}" name="organizationId">
|
||||
<button type="submit" class="btn btn-primary">Enregistrer</button>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<div class="d-flex gap-2">
|
||||
{% if user.pictureUrl is not empty %}
|
||||
<img src="{{ aws_url ~ user.pictureUrl }}" alt="user" class="rounded-circle"
|
||||
<img src="{{asset(user.pictureUrl)}}" alt="user" class="rounded-circle"
|
||||
style="width:40px; height:40px;">
|
||||
{% endif %}
|
||||
<div class="card-title ">
|
||||
|
|
|
|||
Loading…
Reference in New Issue