src/Controller/BusinessContactsController.php line 48
<?phpnamespace App\Controller;use App\Entity\BusinessContacts;use App\Form\BusinessContactsType;use App\Form\ImportType;use App\Repository\BusinessContactsRepository;use App\Repository\BusinessTypesRepository;use App\Services\BusinessContactVCFExport;use App\Services\ImportBusinessContactsService;use App\Services\CompanyDetailsService;use App\Services\CountBusinessContactsService;use App\Services\PhoneAnalyzer;use Doctrine\ORM\EntityManagerInterface;use PhpOffice\PhpSpreadsheet\Spreadsheet;use PhpOffice\PhpSpreadsheet\Writer\Csv;use Psr\Log\LoggerInterface;use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;use Symfony\Bundle\SecurityBundle\Security;use Symfony\Component\HttpFoundation\BinaryFileResponse;use Symfony\Component\HttpFoundation\File\Exception\FileException;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\HttpFoundation\Response;use Symfony\Component\HttpFoundation\ResponseHeaderBag;use Symfony\Component\HttpFoundation\StreamedResponse;use Symfony\Component\Routing\Annotation\Route;use Symfony\Component\String\Slugger\SluggerInterface;/*** @Route("/businesscontacts")*/class BusinessContactsController extends AbstractController{public function __construct(private readonly LoggerInterface $logger){}/*** @Route("/index", name="business_contacts_index", methods={"GET"})*/public function index(Request $request, BusinessContactsRepository $businessContactsRepository, BusinessTypesRepository $businessTypesRepository, CountBusinessContactsService $countBusinessContactsService, PhoneAnalyzer $phoneAnalyzer): Response{// All types (for selectors, etc.) — alpha by label$business_types_all = $businessTypesRepository->findAll();usort($business_types_all, function ($a, $b) {return strcmp($a->getBusinessType(), $b->getBusinessType());});$businessTypeName = $request->query->get('business_type');$adminView = $request->query->get('admin_view');// Determine which types to render as groups + which contacts to includeif ($businessTypeName) {$businessType = $businessTypesRepository->findOneBy(['businessType' => $businessTypeName]);if ($businessType) {// Filtered to a single type$business_contacts = $businessContactsRepository->findBy(['businessType' => $businessType],['id' => 'ASC']);$business_types = [$businessType]; // headings for only this type} else {// Invalid filter: fallback to all$business_contacts = $businessContactsRepository->findAll();$business_types = $businessTypesRepository->findBy([], ['ranking' => 'ASC']);}} else {// No filter: show everything$business_contacts = $businessContactsRepository->findAll();$business_types = $businessTypesRepository->findBy([], ['ranking' => 'ASC']);}// Analyze phones (as before)foreach ($business_contacts as $business_contact) {$business_contact->phoneAnalysis = ['mobile' => $business_contact->getMobile() ? $phoneAnalyzer->analyze($business_contact->getMobile()) : null,'landline' => $business_contact->getLandline() ? $phoneAnalyzer->analyze($business_contact->getLandline()) : null,];}// --- Group + sort within each group: company -> firstName -> lastName ---$norm = function (?string $s): string {return $s === null ? '' : mb_strtolower(trim($s));};$cmp = function ($a, $b) use ($norm): int {// Adjust getter names here if your entity differs$aCompany = $norm(method_exists($a, 'getCompany') ? $a->getCompany() : null);$bCompany = $norm(method_exists($b, 'getCompany') ? $b->getCompany() : null);$aFirst = $norm(method_exists($a, 'getFirstName') ? $a->getFirstName() : null);$bFirst = $norm(method_exists($b, 'getFirstName') ? $b->getFirstName() : null);$aLast = $norm(method_exists($a, 'getLastName') ? $a->getLastName() : null);$bLast = $norm(method_exists($b, 'getLastName') ? $b->getLastName() : null);// Empty values sort AFTER non-empty$emptyOrder = function (string $x, string $y): ?int {$xe = ($x === '');$ye = ($y === '');if ($xe && !$ye) return 1;if (!$xe && $ye) return -1;return null; // both empty or both non-empty -> continue};// Compare companyif (($eo = $emptyOrder($aCompany, $bCompany)) !== null) return $eo;if ($aCompany !== $bCompany) return $aCompany <=> $bCompany;// Compare firstNameif (($eo = $emptyOrder($aFirst, $bFirst)) !== null) return $eo;if ($aFirst !== $bFirst) return $aFirst <=> $bFirst;// Compare lastNameif (($eo = $emptyOrder($aLast, $bLast)) !== null) return $eo;if ($aLast !== $bLast) return $aLast <=> $bLast;// Stable fallback by id$aId = method_exists($a, 'getId') ? (int)$a->getId() : 0;$bId = method_exists($b, 'getId') ? (int)$b->getId() : 0;return $aId <=> $bId;};// Build grouped array keyed by the types we are actually rendering$contacts_by_type = [];foreach ($business_types as $bt) {$contacts_by_type[$bt->getId()] = [];}foreach ($business_contacts as $c) {$type = $c->getBusinessType();if ($type && array_key_exists($type->getId(), $contacts_by_type)) {$contacts_by_type[$type->getId()][] = $c;}}// Sort each groupforeach ($contacts_by_type as &$group) {usort($group, $cmp);}unset($group);// --- end group+sort ---return $this->render('business_contacts/index.html.twig', ['business_contacts' => $business_contacts, // kept for compatibility'business_types' => $business_types,'business_types_all' => $business_types_all,'countBusinessContactsService' => $countBusinessContactsService,'list_or_map' => 'list','admin_check' => $adminView === 'Admin' ? 'Yes' : 'No','current_business_type' => $businessTypeName,'contacts_by_type' => $contacts_by_type, // used by Twig single-table template]);}/*** @Route("/map/{subset}", name="business_contacts_map", methods={"GET"})*/public function map(Request $request, string $subset, BusinessContactsRepository $businessContactsRepository, BusinessTypesRepository $businessTypesRepository, CountBusinessContactsService $countBusinessContacts): Response {// Admins see ALL; others only 'Approved'$isAdmin = $this->isGranted('ROLE_ADMIN');// Build criteria$criteria = [];if (!$isAdmin) {$criteria['status'] = 'Approved';}if ($subset !== 'All') {$business_type = $businessTypesRepository->findOneBy(['businessType' => $subset]);// If the subset name is invalid, return no results instead of errorif ($business_type) {$criteria['businessType'] = $business_type;} else {$business_contacts = [];}}// Fetch contacts (if not already set due to invalid subset)if (!isset($business_contacts)) {$business_contacts = $businessContactsRepository->findBy($criteria);}// Compute map stats (only count rows with valid coords)$latitude_total = 0.0;$longitude_total = 0.0;$count = 0;$latitude_max = -100.0;$latitude_min = 100.0;$longitude_max = -100.0;$longitude_min = 100.0;foreach ($business_contacts as $business_contact) {$lat = $business_contact->getLocationLatitude();$lng = $business_contact->getLocationLongitude();// Must have both coords and not be zeroif ($lat !== null && $lng !== null && (float)$lat !== 0.0 && (float)$lng !== 0.0) {$count++;$latitude_total += (float)$lat;$longitude_total += (float)$lng;if ($lat > $latitude_max) $latitude_max = (float)$lat;if ($lat < $latitude_min) $latitude_min = (float)$lat;if ($lng > $longitude_max) $longitude_max = (float)$lng;if ($lng < $longitude_min) $longitude_min = (float)$lng;}}if ($count === 0) {$latitude_average = 'No data';$longitude_average = 'No data';$latitude_range = 'TBD';$longitude_range = 'TBD';} else {$latitude_average = $latitude_total / $count;$longitude_average = $longitude_total / $count;if ($count === 1) {$latitude_range = 'TBD';$longitude_range = 'TBD';} else {$latitude_range = $latitude_max - $latitude_min;$longitude_range = $longitude_max - $longitude_min;}}$business_types = $businessTypesRepository->findBy([], ['ranking' => 'ASC']);return $this->render('business_contacts/map_of_business_contacts.html.twig', [// 'google_maps_api_key' => $this->getParameter('google_maps_api_key'), // e.g. set in services.yaml or .env'business_contacts' => $business_contacts,'business_types' => $business_types,'subset' => $subset,'latitude_max' => $latitude_max,'latitude_min' => $latitude_min,'latitude_average' => $latitude_average,'latitude_range' => $latitude_range,'longitude_max' => $longitude_max,'longitude_min' => $longitude_min,'longitude_average' => $longitude_average,'longitude_range' => $longitude_range,'count' => $count,'list_or_map' => 'map','admin_check' => $isAdmin ? 'Yes' : 'No',]);}/*** @Route("/new/{business_type}", name="business_contacts_new", methods={"GET", "POST"})*/public function new(Request $request, string $business_type, BusinessContactsRepository $businessContactsRepository, BusinessTypesRepository $businessTypesRepository, CompanyDetailsService $companyDetails, EntityManagerInterface $entityManager): Response{$business_type = $businessTypesRepository->find($business_type);$default_country = $companyDetails->getCompanyDetails()->getCompanyAddressCountry();$businessContact = new BusinessContacts();$businessContact->setBusinessType($business_type);$businessContact->setAddressCountry($default_country);$form = $this->createForm(BusinessContactsType::class, $businessContact);$form->handleRequest($request);if ($form->isSubmitted() && $form->isValid()) {$photo = $form['photo']->getData();if ($photo) {$uniqueId = uniqid(); // Generates a unique ID$uniqueId3digits = substr($uniqueId, 10, 3); // Extracts the first 3 digits$files_name = [];$photo_directory = $this->getParameter('business_contacts_photos_directory');$fileName = pathinfo($photo->getClientOriginalName(), PATHINFO_FILENAME);$file_extension = $photo->guessExtension();$newFileName = $businessContact->getCompany() . "_" . $uniqueId3digits . "." . $file_extension;$photo->move($photo_directory, $newFileName);$businessContact->setPhoto($newFileName);}$file = $form['files']->getData();if ($file) {$file_name = [];$file_directory = $this->getParameter('business_contacts_attachments_directory');$fileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);$file_extension = $file->guessExtension();$newFileName = $fileName . "." . $file_extension;$file->move($file_directory, $newFileName);$businessContact->setFiles($newFileName);}$businessContactsRepository->add($businessContact, true);$firstName = $businessContact->getFirstName();$lastName = $businessContact->getLastName();$entityManager->persist($businessContact);$entityManager->flush();return $this->redirectToRoute('business_contacts_index', [], Response::HTTP_SEE_OTHER);}return $this->renderForm('business_contacts/new.html.twig', ['business_contact' => $businessContact,'form' => $form,]);}/*** @Route("/suggestion", name="business_contacts_suggestion", methods={"GET", "POST"})*/public function suggestion(Request $request, BusinessContactsRepository $businessContactsRepository): Response{$businessContact = new BusinessContacts();$form = $this->createForm(BusinessContactsType::class, $businessContact);$form->handleRequest($request);if ($form->isSubmitted() && $form->isValid()) {$businessContactsRepository->add($businessContact, true);$businessContact->setStatus('Pending');return $this->redirectToRoute('business_contacts_index', [], Response::HTTP_SEE_OTHER);}return $this->renderForm('business_contacts/new.html.twig', ['business_contact' => $businessContact,'form' => $form,]);}/*** @Route("/show/{id}", name="business_contacts_show", methods={"GET"})*/public function show(BusinessContacts $businessContact): Response{$longitude = $businessContact->getLocationLongitude();$latitude = $businessContact->getLocationLatitude();return $this->render('business_contacts/show.html.twig', ['business_contact' => $businessContact,'longitude' => $longitude,'latitude' => $latitude,]);}/*** @Route("/edit/{id}", name="business_contacts_edit", methods={"GET", "POST"})*/public function edit(Request $request, BusinessContacts $businessContact, BusinessContactsRepository $businessContactsRepository): Response{$form = $this->createForm(BusinessContactsType::class, $businessContact);$form->handleRequest($request);if ($form->isSubmitted() && $form->isValid()) {$photo = $form['photo']->getData();if ($photo) {$files_name = [];$photo_directory = $this->getParameter('business_contacts_photos_directory');$fileName = pathinfo($photo->getClientOriginalName(), PATHINFO_FILENAME);$file_extension = $photo->guessExtension();if ($businessContact->getFirstName() == '') {$newFileName = $businessContact->getCompany() . "." . $file_extension;} else {$newFileName = $businessContact->getCompany() . "_" . $businessContact->getFirstName() . "_" . $businessContact->getLastName() . "." . $file_extension;}$photo->move($photo_directory, $newFileName);$businessContact->setPhoto($newFileName);}$file = $form['files']->getData();if ($file) {$file_name = [];$file_directory = $this->getParameter('business_contacts_attachments_directory');$fileName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);$file_extension = $file->guessExtension();$newFileName = $fileName . "." . $file_extension;$file->move($file_directory, $newFileName);$businessContact->setFiles($newFileName);}$businessContactsRepository->add($businessContact, true);return $this->redirectToRoute('business_contacts_index', [], Response::HTTP_SEE_OTHER);}return $this->renderForm('business_contacts/edit.html.twig', ['business_contact' => $businessContact,'form' => $form,]);}/*** @Route("/delete/{id}", name="business_contacts_delete", methods={"POST"})*/public function delete(Request $request, BusinessContacts $businessContact, BusinessContactsRepository $businessContactsRepository, EntityManagerInterface $entityManager): Response{$referer = $request->headers->get('referer');$file_name = $businessContact->getFiles();if ($file_name) {$file = $this->getParameter('business_contacts_attachments_directory') . $file_name;if (file_exists($file)) {unlink($file);}$businessContact->setFiles('');$entityManager->flush();}$photo_file_name = $businessContact->getPhoto();if ($photo_file_name) {$photo_file_name = $this->getParameter('business_contacts_photos_directory') . $photo_file_name;if (file_exists($photo_file_name)) {unlink($photo_file_name);}$businessContact->setPhoto('');$entityManager->flush();}if ($this->isCsrfTokenValid('delete' . $businessContact->getId(), $request->request->get('_token'))) {$businessContactsRepository->remove($businessContact, true);}return $this->redirect($referer);}/*** @Route("/delete_GPS_location/{id}", name="business_contacts_delete_GPS_location", methods={"GET","POST"})*/public function deleteGPSLocation(Request $request, int $id, BusinessContactsRepository $businessContactsRepository, EntityManagerInterface $entityManager): Response{$referer = $request->headers->get('referer');$businessContact = $businessContactsRepository->find($id);$businessContact->setLocationLatitude(null);$businessContact->setLocationLongitude(null);$entityManager->flush();return $this->redirect($referer ?: $this->generateUrl('business_contacts_index'));}/*** @Route("/delete_all_GPS_locations", name="business_contacts_delete_all_GPS_locations")*/public function deleteAllBusinessContactsGPSLocations(Request $request, BusinessContactsRepository $businessContactsRepository, EntityManagerInterface $entityManager): Response{$referer = $request->headers->get('referer');$business_contacts = $businessContactsRepository->findAll();foreach ($business_contacts as $business_contact) {$business_contact->setLocationLongitude(null);$business_contact->setLocationLatitude(null);$entityManager->flush();}return $this->redirect($referer ?: $this->generateUrl('business_contacts_index'));}/*** @Route("/delete_all", name="business_contacts_delete_all")*/public function deleteAllBusinessContacts(BusinessContactsRepository $businessContactsRepository, EntityManagerInterface $entityManager): Response{$business_contacts = $businessContactsRepository->findAll();foreach ($business_contacts as $business_contact) {$entityManager->remove($business_contact);$entityManager->flush();}return $this->redirectToRoute('business_contacts_index', [], Response::HTTP_SEE_OTHER);}/*** @Route("/delete_photo_file/{id}", name="business_contact_delete_photo_file", methods={"POST", "GET"})*/public function deleteBusinessContactPhotoFile(int $id, Request $request, BusinessContacts $businessContact, EntityManagerInterface $entityManager){$referer = $request->headers->get('referer');$photo_file_name = $businessContact->getPhoto();if ($photo_file_name) {$photo_file_name = $this->getParameter('business_contacts_photos_directory') . "/" . $photo_file_name;if (file_exists($photo_file_name)) {unlink($photo_file_name);}$businessContact->setPhoto('');$entityManager->flush();}return $this->redirect($referer);}/*** @Route("/delete_attachment_file/{id}", name="business_contact_delete_attachment_file", methods={"POST", "GET"})*/public function deleteBusinessContactAttachmentFile(int $id, Request $request, BusinessContacts $businessContact, EntityManagerInterface $entityManager){$referer = $request->headers->get('referer');$file_name = $businessContact->getFiles();if ($file_name) {$file = $this->getParameter('business_contacts_attachments_directory') . "/" . $file_name;if (file_exists($file)) {unlink($file);}$businessContact->setFiles('');$entityManager->flush();}return $this->redirect($referer);}/*** @Route ("/view/photo/{id}", name="view_business_contact_photo")*/public function viewBusinessContactPhoto(Request $request, $id, BusinessContactsRepository $businessContactsRepository){$business_contact = $businessContactsRepository->find($id);return $this->render('business_contacts/view_photo.html.twig', ['business_contact' => $business_contact]);}/*** @Route ("/business_contacts_export", name="business_contacts_export" )*/public function businessContactsExport(BusinessContactsRepository $businessContactsRepository){$data = [];$exported_date = new \DateTime('now');$exported_date_formatted = $exported_date->format('d-M-Y');$fileName = 'business_contacts_export_' . $exported_date_formatted . '.csv';$count = 0;$business_contact_list = $businessContactsRepository->findAll();foreach ($business_contact_list as $business_contact) {$data[] = ["BusinessContacts",$business_contact->getStatus(),$business_contact->getBusinessOrPerson(),$business_contact->getBusinessType()->getBusinessType(),$business_contact->getCompany(),$business_contact->getFirstName(),$business_contact->getLastName(),$business_contact->getWebsite(),$business_contact->getEmail(),$business_contact->getLandline(),$business_contact->getMobile(),$business_contact->getAddressStreet(),$business_contact->getAddressCity(),$business_contact->getAddressCounty(),$business_contact->getAddressPostCode(),$business_contact->getAddressCountry(),$business_contact->getLocationLongitude(),$business_contact->getLocationLatitude(),$business_contact->getNotes()];}$spreadsheet = new Spreadsheet();$sheet = $spreadsheet->getActiveSheet();$sheet->setTitle('Business Contacts');$sheet->getCell('A1')->setValue('Entity');$sheet->getCell('B1')->setValue('Status');$sheet->getCell('C1')->setValue('Business Or Person');$sheet->getCell('D1')->setValue('Business Type');$sheet->getCell('E1')->setValue('Company');$sheet->getCell('F1')->setValue('First Name');$sheet->getCell('G1')->setValue('Last Name');$sheet->getCell('H1')->setValue('Web Page');$sheet->getCell('I1')->setValue('E-mail');$sheet->getCell('J1')->setValue('Business Phone');$sheet->getCell('K1')->setValue('Mobile Phone');$sheet->getCell('L1')->setValue('Business Street');$sheet->getCell('M1')->setValue('Business City');$sheet->getCell('N1')->setValue('Business County');$sheet->getCell('O1')->setValue('Business Postal Code');$sheet->getCell('P1')->setValue('Business Country/Region');$sheet->getCell('Q1')->setValue('Location Longitude');$sheet->getCell('R1')->setValue('Location Latitude');$sheet->getCell('S1')->setValue('Notes');$sheet->fromArray($data, null, 'A2', true);$total_rows = $sheet->getHighestRow();for ($i = 2; $i <= $total_rows; $i++) {$cell = "L" . $i;$sheet->getCell($cell)->getHyperlink()->setUrl("https://google.com");}$writer = new Csv($spreadsheet);$response = new StreamedResponse(function () use ($writer) {$writer->save('php://output');});$response->headers->set('Content-Type', 'application/vnd.ms-excel');$response->headers->set('Content-Disposition', sprintf('attachment;filename="%s"', $fileName));$response->headers->set('Cache-Control', 'max-age=0');return $response;}/*** Export all BusinessContacts as a ZIP of individual .vcf files.** @Route("/business_contacts_export_vcf_zip", name="business_contacts_export_vcf_zip", methods={"GET"})*/public function exportBusinessContactsAsZip(BusinessContactsRepository $businessContactsRepository, BusinessContactVCFExport $businessContactVCFExport): Response{$contacts = $businessContactsRepository->findAll();if (!$contacts) {return new Response('No contacts to export.', Response::HTTP_NO_CONTENT);}// Final file name: business_contacts_YYYYMMDD.zip$datePart = (new \DateTimeImmutable('now'))->format('Ymd');$baseName = "business_contacts_{$datePart}";$exportDir = rtrim($businessContactVCFExport->getExportDirectory(), "/\\");$zipPath = $exportDir . DIRECTORY_SEPARATOR . $baseName . '.zip';// Ensure export dir is OKif (!is_dir($exportDir) || !is_writable($exportDir)) {$msg = "Export directory is not writable: {$exportDir}";$this->logger->error($msg);return new Response($msg, Response::HTTP_INTERNAL_SERVER_ERROR);}// Make name unique if neededif (file_exists($zipPath)) {$i = 1;do {$zipPath = $exportDir . DIRECTORY_SEPARATOR . $baseName . '-' . $i . '.zip';$i++;} while (file_exists($zipPath));}$downloadName = basename($zipPath); // what the user downloads// Create temp dir for individual VCFs$tempDir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'bc_vcards_' . bin2hex(random_bytes(6));if (!@mkdir($tempDir, 0777, true) && !is_dir($tempDir)) {$msg = "Failed to create temporary directory: {$tempDir}";$this->logger->error($msg);return new Response($msg, Response::HTTP_INTERNAL_SERVER_ERROR);}try {// Write each contact as its own .vcf using the service's BusinessContacts generatorforeach ($contacts as $c) {$first = trim((string)($c->getFirstName() ?? ''));$last = trim((string)($c->getLastName() ?? ''));$comp = trim((string)($c->getCompany() ?? ''));$label = $comp !== '' ? $comp : trim($first . ' ' . $last);if ($label === '') {$label = 'contact';}$safe = preg_replace('/[^a-zA-Z0-9._-]/', '_', $label);$vcfPath = $tempDir . DIRECTORY_SEPARATOR . $safe . '.vcf';// This writes the file to $vcfPath (and returns its contents)$businessContactVCFExport->generateVCard($c, $vcfPath);}// Zip them up$zip = new \ZipArchive();if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {throw new \RuntimeException("Cannot create ZIP archive at: {$zipPath}");}foreach (glob($tempDir . DIRECTORY_SEPARATOR . '*.vcf') as $file) {$zip->addFile($file, basename($file));}$zip->close();} catch (\Throwable $e) {$this->logger->error('Failed to generate ZIP of vCards: ' . $e->getMessage(), ['exception' => $e]);// Cleanup temp dir on failureforeach (glob($tempDir . DIRECTORY_SEPARATOR . '*') ?: [] as $f) {@unlink($f);}@rmdir($tempDir);return new Response('Failed to generate ZIP: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);}// Cleanup temp dir (ZIP stays until sent)foreach (glob($tempDir . DIRECTORY_SEPARATOR . '*') ?: [] as $f) {@unlink($f);}@rmdir($tempDir);/** @var BinaryFileResponse $response */$response = $this->file($zipPath, $downloadName, ResponseHeaderBag::DISPOSITION_ATTACHMENT);// Delete the ZIP after it's been sent to the client$response->deleteFileAfterSend(true);return $response;}/*** @Route ("/business_contacts_import", name="business_contacts_import" )*/public function businessContactsImport(Request $request, SluggerInterface $slugger, BusinessContactsRepository $businessContactsRepository, ImportBusinessContactsService $businessContactsImportService): Response{$form = $this->createForm(ImportType::class);$form->handleRequest($request);if ($form->isSubmitted() && $form->isValid()) {$importFile = $form->get('File')->getData();if ($importFile) {$originalFilename = pathinfo($importFile->getClientOriginalName(), PATHINFO_FILENAME);$safeFilename = $slugger->slug($originalFilename);$newFilename = $safeFilename . '.' . 'csv';try {$importFile->move($this->getParameter('business_contacts_import_directory'),$newFilename);} catch (FileException $e) {die('Import failed');}$businessContactsImportService->importBusinessContacts($newFilename);return $this->redirectToRoute('business_contacts_index');}}return $this->render('home/import.html.twig', ['form' => $form->createView(),'heading' => 'Business Contacts Import',]);}/*** @Route("/update/location", name="update_business_contact_location", methods={"POST"})*/public function updateLocation(BusinessContactsRepository $businessContactsRepository, EntityManagerInterface $manager): Response{$id = $_POST['id'];$latitude = $_POST['latitude'];$longitude = $_POST['longitude'];$gps = $latitude . ',' . $longitude;$business_contact = $businessContactsRepository->find($id);$business_contact->setLocationLongitude($longitude)->setLocationLatitude($latitude);$manager->flush();return new Response(null);}/*** @Route("/gps_location_clear/{id}", name="business_contact_clear_gps_location")*/publicfunction clearGPSLocation(Request $request, BusinessContacts $businessContacts, EntityManagerInterface $entityManager){$referer = $request->headers->get('referer');$businessContacts->setLocationLongitude(null);$businessContacts->setLocationLatitude(null);$entityManager->flush();return $this->redirect($referer);}/*** @Route("/show_attachment/{id}", name="business_contact_show_attachment")*/public function showAttachmentBusinessContact(int $id, BusinessContactsRepository $businessContactsRepository){$business_contact = $businessContactsRepository->find($id);$filename = $business_contact->getFiles();$filepath = $this->getParameter('business_contacts_attachments_directory') . "/" . $filename;if (file_exists($filepath)) {$response = new BinaryFileResponse($filepath);$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_INLINE, //use ResponseHeaderBag::DISPOSITION_ATTACHMENT to save as an attachment$filename);return $response;} else {return new Response("file does not exist");}}// /**// * @Route("/create/Vcard/{id}", name="create_vcard")// */// public function createVcard(int $id, BusinessContactsRepository $businessContactsRepository)// {// $business_contact = $businessContactsRepository->find($id);// $vcard = new VCard();// $businessOrPerson = $business_contact->getBusinessOrPerson();// $business_type = $business_contact->getBusinessType()->getBusinessType();// $firstName = $business_contact->getFirstName();// $lastName = $business_contact->getLastName();// $mobile = $business_contact->getMobile();// $landline = $business_contact->getLandline();// $company = $business_contact->getCompany();// $website = $business_contact->getWebsite();// $addressStreet = $business_contact->getAddressStreet();// $addressCity = $business_contact->getAddressCity();// $addressPostCode = $business_contact->getAddressPostcode();// $addressCountry = $business_contact->getAddressCountry();// $longitude = $business_contact->getLocationLongitude();// $latitude = $business_contact->getLocationLatitude();// $notes = $business_contact->getNotes();//// if ($businessOrPerson = "Business") {// $firstNameCard = $company;// $lastNameCard = $business_type;// $companyCard = [];// }// if ($businessOrPerson = "Person") {// $firstNameCard = $firstName;// $lastNameCard = $lastName;// $companyCard = $company;// }// $vcard->addName($lastNameCard, $firstNameCard);// $vcard->addEmail($business_contact->getEmail())// ->addPhoneNumber($landline, 'work')// ->addPhoneNumber($mobile, 'mobile')// ->addCompany($companyCard)// ->addURL($website)// ->addNote($notes)// ->addAddress($name = '', $extended = '', $street = $addressStreet, $city = $addressCity, $region = '', $zip = $addressPostCode, $country = $addressCountry, $type = 'WORK;POSTAL');// $vcard->download();// return new Response(null);// }/*** Export a single BusinessContact as vCard with embedded photo.* @Route("/create_vcard_business_contact/{id}", name="create_vcard_business_contact", methods={"GET"})*/public function createVCFBusinessContact(Request $request,Security $security,BusinessContacts $businessContacts,BusinessContactVCFExport $exporter): Response{// Decide filename based on Business vs Person$businessOrPerson = (string)($businessContacts->getBusinessOrPerson() ?? 'Person');$businessType = method_exists($businessContacts, 'getBusinessType') && $businessContacts->getBusinessType()? (string)$businessContacts->getBusinessType()->getBusinessType(): '';if (strcasecmp($businessOrPerson, 'Business') === 0) {$baseA = (string)($businessContacts->getCompany() ?? 'Company');$baseB = $businessType !== '' ? $businessType : 'Business';} else {$baseA = (string)($businessContacts->getFirstName() ?? 'Unknown');$baseB = (string)($businessContacts->getLastName() ?? 'Contact');}// Sanitize but keep the comma between parts$safe = fn(string $s) => preg_replace('/[^\w\-\.\s]/u', '', $s); // allow letters, numbers, _, -, ., space$fileName = sprintf('%s,%s.vcf', $safe($baseA), $safe($baseB));$exportDir = $exporter->getExportDirectory();if (!is_dir($exportDir) || !is_writable($exportDir)) {return new Response("Export directory is not writable: $exportDir", Response::HTTP_INTERNAL_SERVER_ERROR);}$filePath = rtrim($exportDir, '/\\') . DIRECTORY_SEPARATOR . $fileName;try {$exporter->generateVCard($businessContacts, $filePath);} catch (\Throwable $e) {return new Response('Failed to generate vCard: ' . $e->getMessage(), Response::HTTP_INTERNAL_SERVER_ERROR);}if (!is_file($filePath) || filesize($filePath) === 0) {return new Response('vCard file was not created or is empty.', Response::HTTP_INTERNAL_SERVER_ERROR);}// Force download with your exact requested filenamereturn $this->file($filePath, $fileName, ResponseHeaderBag::DISPOSITION_ATTACHMENT);}}