Spaces:
No application file
No application file
declare(strict_types=1); | |
namespace Mautic\LeadBundle\Model; | |
use Doctrine\ORM\EntityManager; | |
use Mautic\CoreBundle\Helper\CoreParametersHelper; | |
use Mautic\CoreBundle\Helper\ExportHelper; | |
use Mautic\CoreBundle\Helper\UserHelper; | |
use Mautic\CoreBundle\Model\AbstractCommonModel; | |
use Mautic\CoreBundle\Model\IteratorExportDataModel; | |
use Mautic\CoreBundle\Security\Permissions\CorePermissions; | |
use Mautic\CoreBundle\Translation\Translator; | |
use Mautic\EmailBundle\Helper\MailHelper; | |
use Mautic\LeadBundle\Entity\ContactExportScheduler; | |
use Mautic\LeadBundle\Entity\ContactExportSchedulerRepository; | |
use Mautic\UserBundle\Entity\User; | |
use Psr\Log\LoggerInterface; | |
use Symfony\Component\EventDispatcher\EventDispatcherInterface; | |
use Symfony\Component\HttpFoundation\BinaryFileResponse; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\RequestStack; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpFoundation\Session\SessionInterface; | |
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException; | |
use Symfony\Component\Routing\Generator\UrlGeneratorInterface; | |
/** | |
* @extends AbstractCommonModel<ContactExportScheduler> | |
*/ | |
class ContactExportSchedulerModel extends AbstractCommonModel | |
{ | |
private const EXPORT_FILE_NAME_DATE_FORMAT = 'Y_m_d_H_i_s'; | |
public function __construct( | |
private SessionInterface $session, | |
private RequestStack $requestStack, | |
private LeadModel $leadModel, | |
private ExportHelper $exportHelper, | |
private MailHelper $mailHelper, | |
EntityManager $em, | |
CorePermissions $security, | |
EventDispatcherInterface $dispatcher, | |
UrlGeneratorInterface $router, | |
Translator $translator, | |
UserHelper $userHelper, | |
LoggerInterface $mauticLogger, | |
CoreParametersHelper $coreParametersHelper | |
) { | |
parent::__construct($em, $security, $dispatcher, $router, $translator, $userHelper, $mauticLogger, $coreParametersHelper); | |
} | |
public function getRepository(): ContactExportSchedulerRepository | |
{ | |
/** @var ContactExportSchedulerRepository $repo */ | |
$repo = $this->em->getRepository(ContactExportScheduler::class); | |
return $repo; | |
} | |
/** | |
* @param array<mixed> $permissions | |
* | |
* @return array<string, mixed> | |
*/ | |
public function prepareData(array $permissions): array | |
{ | |
$search = $this->session->get('mautic.lead.filter', ''); | |
$orderBy = $this->session->get('mautic.lead.orderby', 'l.last_active'); | |
$orderByDir = $this->session->get('mautic.lead.orderbydir', 'DESC'); | |
$indexMode = $this->session->get('mautic.lead.indexmode', 'list'); | |
$anonymous = $this->translator->trans('mautic.lead.lead.searchcommand.isanonymous'); | |
/** @var Request $request */ | |
$request = $this->getRequest(); | |
$ids = $request->get('ids'); | |
$fileType = $request->get('filetype', 'csv'); | |
$filter = ['string' => $search, 'force' => []]; | |
if (!empty($ids)) { | |
$filter['force'] = [ | |
[ | |
'column' => 'l.id', | |
'expr' => 'in', | |
'value' => json_decode($ids, true, 512, JSON_THROW_ON_ERROR), | |
], | |
]; | |
} else { | |
if ('list' !== $indexMode || (!str_contains($search, $anonymous))) { | |
// Remove anonymous leads unless requested to prevent clutter. | |
$filter['force'] = [ | |
[ | |
'column' => 'l.dateIdentified', | |
'expr' => 'isNotNull', | |
], | |
]; | |
} | |
if (!$permissions['lead:leads:viewother']) { | |
// Show only owner's contacts. | |
$filter['force'] = [ | |
[ | |
'column' => 'l.owner', | |
'expr' => 'eq', | |
], | |
]; | |
} | |
} | |
return [ | |
'start' => 0, | |
'limit' => $this->coreParametersHelper->get('contact_export_batch_size', 1000), | |
'filter' => $filter, | |
'orderBy' => $orderBy, | |
'orderByDir' => $orderByDir, | |
'withTotalCount' => true, | |
'fileType' => $fileType, | |
]; | |
} | |
/** | |
* @param array<mixed> $data | |
*/ | |
public function saveEntity(array $data): ContactExportScheduler | |
{ | |
$contactExportScheduler = new ContactExportScheduler(); | |
$contactExportScheduler | |
->setUser($this->userHelper->getUser()) | |
->setScheduledDateTime(new \DateTimeImmutable()) | |
->setData($data); | |
$this->em->persist($contactExportScheduler); | |
$this->em->flush(); | |
return $contactExportScheduler; | |
} | |
public function processAndGetExportFilePath(ContactExportScheduler $contactExportScheduler): string | |
{ | |
$data = $contactExportScheduler->getData(); | |
$fileType = $data['fileType']; | |
$resultsCallback = fn ($contact) => $contact->getProfileFields(); | |
$iterator = new IteratorExportDataModel( | |
$this->leadModel, | |
$contactExportScheduler->getData(), | |
$resultsCallback, | |
true | |
); | |
/** @var \DateTimeImmutable $scheduledDateTime */ | |
$scheduledDateTime = $contactExportScheduler->getScheduledDateTime(); | |
$fileName = 'contacts_export_'.$scheduledDateTime->format(self::EXPORT_FILE_NAME_DATE_FORMAT); | |
return $this->exportResultsAs($iterator, $fileType, $fileName); | |
} | |
public function getEmailMessageWithLink(string $filePath): string | |
{ | |
$link = $this->router->generate( | |
'mautic_contact_export_download', | |
['fileName' => basename($filePath)], | |
UrlGeneratorInterface::ABSOLUTE_URL | |
); | |
return $this->translator->trans( | |
'mautic.lead.export.email', | |
['%link%' => $link, '%label%' => basename($filePath)] | |
); | |
} | |
public function sendEmail(ContactExportScheduler $contactExportScheduler, string $filePath): void | |
{ | |
/** @var User $user */ | |
$user = $contactExportScheduler->getUser(); | |
$message = $this->getEmailMessageWithLink($filePath); | |
$this->mailHelper->setTo([$user->getEmail() => $user->getName()]); | |
$this->mailHelper->setSubject( | |
$this->translator->trans('mautic.lead.export.email_subject', ['%file_name%' => basename($filePath)]) | |
); | |
$this->mailHelper->setBody($message); | |
$this->mailHelper->parsePlainText($message); | |
$this->mailHelper->send(true); | |
} | |
public function deleteEntity(ContactExportScheduler $contactExportScheduler): void | |
{ | |
$this->em->remove($contactExportScheduler); | |
$this->em->flush(); | |
} | |
public function getExportFileToDownload(string $fileName): BinaryFileResponse | |
{ | |
$filePath = $this->coreParametersHelper->get('contact_export_dir').'/'.$fileName; | |
$contentType = $this->getContactExportFileContentType($fileName); | |
return new BinaryFileResponse( | |
$filePath, | |
Response::HTTP_OK, | |
[ | |
'Content-Type' => $contentType, | |
'Content-Disposition' => 'attachment; filename="'.$fileName.'"', | |
'Expires' => 0, | |
'Cache-Control' => 'must-revalidate', | |
'Pragma' => 'public', | |
] | |
); | |
} | |
private function getRequest(): ?Request | |
{ | |
return $this->requestStack->getCurrentRequest(); | |
} | |
private function exportResultsAs(IteratorExportDataModel $iterator, string $fileType, string $fileName): string | |
{ | |
if (!in_array($fileType, $this->exportHelper->getSupportedExportTypes(), true)) { | |
throw new BadRequestHttpException($this->translator->trans('mautic.error.invalid.export.type', ['%type%' => $fileType])); | |
} | |
$csvFilePath = $this->exportHelper | |
->exportDataIntoFile($iterator, $fileType, strtolower($fileName.'.'.$fileType)); | |
return $this->exportHelper->zipFile($csvFilePath, 'contacts_export.csv'); | |
} | |
private function getContactExportFileContentType(string $fileName): string | |
{ | |
$ext = pathinfo($fileName, PATHINFO_EXTENSION); | |
if ('zip' === $ext) { | |
return 'application/zip'; | |
} | |
throw new BadRequestHttpException($this->translator->trans('mautic.error.invalid.specific.export.type', ['%type%' => $ext, '%expected_type%' => 'zip'])); | |
} | |
} | |