248 lines
10 KiB
PHP
248 lines
10 KiB
PHP
<?php
|
|
|
|
namespace App\Service;
|
|
|
|
use App\Entity\Apps;
|
|
use App\Entity\Organizations;
|
|
use App\Entity\Roles;
|
|
use App\Entity\UserOrganizatonApp;
|
|
use App\Entity\UsersOrganizations;
|
|
use App\Repository\UsersOrganizationsRepository;
|
|
use App\Service\LoggerService;
|
|
use Doctrine\ORM\EntityManagerInterface;
|
|
use Psr\Log\LoggerInterface;
|
|
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
|
|
|
class OrganizationsService
|
|
{
|
|
private string $logoDirectory;
|
|
|
|
public function __construct(
|
|
string $logoDirectory, private readonly AwsService $awsService,
|
|
private readonly EntityManagerInterface $entityManager,
|
|
private readonly UsersOrganizationsRepository $uoRepository,
|
|
private readonly NotificationService $notificationService,
|
|
private readonly LoggerInterface $emailNotificationLogger, private readonly LoggerService $loggerService,
|
|
)
|
|
{
|
|
$this->logoDirectory = $logoDirectory;
|
|
}
|
|
|
|
public function handleLogo(Organizations $organization, $logoFile): void
|
|
{
|
|
// 1. Define the destination directory (adjust path as needed, e.g., 'public/uploads/profile_pictures')
|
|
$destinationDir = 'uploads/organization_logos';
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
$extension = $logoFile->guessExtension();
|
|
|
|
// Sanitize the filename to remove special characters/spaces to prevent filesystem errors
|
|
$safeName = preg_replace('/[^a-zA-Z0-9]/', '', $organization->getName());
|
|
$customFilename = $safeName . '_' . date('dmyHis') . '.' . $extension;
|
|
|
|
try {
|
|
// 4. Move the file to the destination directory
|
|
// The move() method is standard in Symfony/Laravel UploadedFile objects
|
|
$logoFile->move($destinationDir, $customFilename);
|
|
|
|
// 5. Update the user entity with the relative path
|
|
// Ensure you store the path relative to your public folder usually
|
|
$organization->setLogoUrl('/'.$destinationDir . '/' . $customFilename);
|
|
|
|
} catch (\Exception $e) {
|
|
// 6. Log the critical error as requested
|
|
$this->loggerService->logError('File upload failed',[
|
|
'target_organization_id' => $organization->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 deleteLogo(Organizations $organization): void
|
|
{
|
|
// 1. Get the current picture path from the user entity
|
|
$currentPicturePath = $organization->getLogoUrl();
|
|
|
|
// 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_organization_id' => $organization->getId(),
|
|
'missing_path' => $currentPicturePath
|
|
]);
|
|
}
|
|
|
|
// 4. Update the user entity to remove the reference
|
|
$organization->setLogoUrl("");
|
|
|
|
} 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_organization_id' => $organization->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.');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merge all apps with org apps and add a "hasAccess" flag.
|
|
*
|
|
* @param array $appsAll
|
|
* @param array $apps
|
|
* @return array
|
|
*/
|
|
public function appsAccess(array $appsAll, array $apps): array
|
|
{
|
|
// Build a quick lookup of app IDs the org has access to
|
|
$orgAppIds = array_map(static fn(Apps $app) => $app->getId(), $apps);
|
|
|
|
$result = [];
|
|
foreach ($appsAll as $app) {
|
|
$result[] = [
|
|
'entity' => $app, // Keep the full entity for Twig
|
|
'hasAccess' => in_array($app->getId(), $orgAppIds, true),
|
|
];
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
public function notifyOrganizationAdmins(array $data, string $type): void
|
|
{
|
|
|
|
$roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']);
|
|
|
|
$adminUOs = $this->uoRepository->findBy(['organization' => $data['organization'], 'isActive' => true]);
|
|
|
|
foreach ($adminUOs as $adminUO) {
|
|
$uoa = $this->entityManager->getRepository(UserOrganizatonApp::class)
|
|
->findOneBy([
|
|
'userOrganization' => $adminUO,
|
|
'role' => $roleAdmin,
|
|
'isActive' => true
|
|
]);
|
|
switch ($type) {
|
|
case 'USER_ACCEPTED':
|
|
if ($uoa && $adminUO->getUsers()->getId() !== $data['user']->getId() ) {
|
|
$newUser = $data['user'];
|
|
$this->notificationService->notifyUserAcceptedInvite(
|
|
$adminUO->getUsers(),
|
|
$newUser,
|
|
$data['organization']
|
|
);
|
|
$this->loggerService->logAdminNotified([
|
|
'admin_user_id' =>$adminUO->getUsers()->getId(),
|
|
'target_user_id' => $newUser->getId(),
|
|
'organization_id' => $data['organization']->getId(),'case' =>$type]);
|
|
}
|
|
break;
|
|
case 'USER_INVITED':
|
|
if ($uoa) {
|
|
$invitedUser = $data['user'];
|
|
$this->notificationService->notifyUserInvited(
|
|
$adminUO->getUsers(),
|
|
$invitedUser,
|
|
$data['organization']
|
|
);
|
|
$this->loggerService->logAdminNotified([
|
|
'admin_user_id' =>$adminUO->getUsers()->getId(),
|
|
'target_user_id' => $invitedUser->getId(),
|
|
'organization_id' => $data['organization']->getId(),'case' =>$type]);
|
|
}
|
|
|
|
break;
|
|
case 'USER_DEACTIVATED':
|
|
if ($uoa && $adminUO->getUsers()->getId() !== $data['user']->getId() ) {
|
|
$removedUser = $data['user'];
|
|
$this->notificationService->notifyUserDeactivated(
|
|
$adminUO->getUsers(),
|
|
$removedUser,
|
|
$data['organization']
|
|
);
|
|
$this->loggerService->logAdminNotified([
|
|
'admin_user_id' =>$adminUO->getUsers()->getId(),
|
|
'target_user_id' => $removedUser->getId(),
|
|
'organization_id' => $data['organization']->getId(),'case' =>$type]);
|
|
}
|
|
|
|
break;
|
|
case 'USER_DELETED':
|
|
if ($uoa && $adminUO->getUsers()->getId() !== $data['user']->getId() ) {
|
|
$removedUser = $data['user'];
|
|
$this->notificationService->notifyUserDeleted(
|
|
$adminUO->getUsers(),
|
|
$removedUser,
|
|
$data['organization']
|
|
);
|
|
$this->loggerService->logAdminNotified([
|
|
'admin_user_id' =>$adminUO->getUsers()->getId(),
|
|
'target_user_id' => $removedUser->getId(),
|
|
'organization_id' => $data['organization']->getId(),'case' =>$type]);
|
|
}
|
|
break;
|
|
case 'USER_ACTIVATED':
|
|
if ($uoa && $adminUO->getUsers()->getId() !== $data['user']->getId() ) {
|
|
$activatedUser = $data['user'];
|
|
$this->notificationService->notifyUserActivated(
|
|
$adminUO->getUsers(),
|
|
$activatedUser,
|
|
$data['organization']
|
|
);
|
|
$this->loggerService->logAdminNotified([
|
|
'admin_user_id' =>$adminUO->getUsers()->getId(),
|
|
'target_user_id' => $activatedUser->getId(),
|
|
'organization_id' => $data['organization']->getId(),'case' =>$type]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
/* Function that check if the project prefix was provided and if it is unique, if not it will generate a random one and check again until it is unique */
|
|
public function generateUniqueProjectPrefix(): string{
|
|
$prefix = $this->generateRandomPrefix();
|
|
while ($this->entityManager->getRepository(Organizations::class)->findOneBy(['projectPrefix' => $prefix])) {
|
|
$prefix = $this->generateRandomPrefix();
|
|
}
|
|
return $prefix;
|
|
}
|
|
|
|
private function generateRandomPrefix(): string
|
|
{
|
|
return substr(str_shuffle('abcdefghijklmnopqrstuvwxyz'), 0, 4);
|
|
}
|
|
|
|
|
|
}
|