Spaces:
No application file
No application file
namespace Mautic\CoreBundle\Command; | |
use Doctrine\ORM\EntityManager; | |
use Mautic\CoreBundle\IpLookup\DoNotSellList\MaxMindDoNotSellList; | |
use Mautic\LeadBundle\Entity\Lead; | |
use Mautic\LeadBundle\Entity\LeadRepository; | |
use Symfony\Component\Console\Command\Command; | |
use Symfony\Component\Console\Helper\ProgressBar; | |
use Symfony\Component\Console\Input\InputInterface; | |
use Symfony\Component\Console\Input\InputOption; | |
use Symfony\Component\Console\Output\OutputInterface; | |
/** | |
* CLI Command to purge data from Mautic that appears on the | |
* MaxMind Do Not Sell list. | |
*/ | |
class MaxMindDoNotSellPurgeCommand extends Command | |
{ | |
public function __construct( | |
private EntityManager $em, | |
private LeadRepository $leadRepository, | |
private MaxMindDoNotSellList $doNotSellList | |
) { | |
parent::__construct(); | |
} | |
protected function configure() | |
{ | |
$this->setName('mautic:max-mind:purge') | |
->addOption( | |
'dry-run', | |
'd', | |
InputOption::VALUE_NONE, | |
'Get a list of data that will be purged.' | |
) | |
->setHelp(<<<'EOT' | |
The <info>%command.name%</info> command will purge all data from Mautic which is related to any IP found on the MaxMind Do Not Sell List. | |
<info>php %command.full_name% --dry-run</info> | |
Performs a dry-run which will not actually purge any data, but will produce a list of what would be purged. | |
<info>php %command.full_name% --batch-size</info> | |
Set the number of records to return in a batch when processing the Do Not Sell List. This option is ignored if IPs are passed as an argument. | |
EOT | |
); | |
} | |
protected function execute(InputInterface $input, OutputInterface $output): int | |
{ | |
try { | |
$dryRun = $input->getOption('dry-run'); | |
$output->writeln('<info>Step 1: Searching for contacts with data from Do Not Sell List...</info>'); | |
$this->doNotSellList->loadList(); | |
$doNotSellListIPs = array_map(fn ($item): string => | |
// strip subnet mask characters | |
$this->doNotSellList->stripCIDR($item['value']), $this->doNotSellList->getList()); | |
$doNotSellContacts = $this->findContactsFromIPs($doNotSellListIPs); | |
if (0 == count($doNotSellContacts)) { | |
$output->writeln('<info>No matches found.</info>'); | |
return Command::SUCCESS; | |
} | |
$output->writeln('Found '.count($doNotSellContacts)." contacts with an IP from the Do Not Sell list.\n"); | |
if ($dryRun) { | |
$output->writeln('<info>Dry run; skipping purge.</info>'); | |
return Command::SUCCESS; | |
} | |
$output->writeln('<info>Step 2: Purging data...</info>'); | |
$purgeProgress = new ProgressBar($output, count($doNotSellContacts)); | |
foreach ($doNotSellContacts as $contact) { | |
$this->purgeData($contact['id'], $contact['ip_address']); | |
$purgeProgress->advance(1); | |
} | |
$purgeProgress->finish(); | |
$output->writeln("\n<info>Purge complete.</info>\n"); | |
return Command::SUCCESS; | |
} catch (\Exception $e) { | |
$output->writeln("\n<error>".$e->getMessage().'</error>'); | |
return Command::FAILURE; | |
} | |
} | |
private function findContactsFromIPs(array $ips): array | |
{ | |
$in = "'".implode("','", $ips)."'"; | |
$sql = | |
'SELECT x.lead_id AS id, ip.ip_address AS ip_address '. | |
'FROM '.MAUTIC_TABLE_PREFIX.'lead_ips_xref x '. | |
'JOIN '.MAUTIC_TABLE_PREFIX.'ip_addresses ip ON x.ip_id = ip.id '. | |
'WHERE ip.ip_address IN ('.$in.')'; | |
$conn = $this->em->getConnection(); | |
$stmt = $conn->prepare($sql); | |
$result = $stmt->executeQuery(); | |
return $result->fetchAllAssociative(); | |
} | |
private function purgeData(string $contactId, string $ip): bool | |
{ | |
/** @var Lead $lead */ | |
$lead = $this->leadRepository->findOneBy(['id' => $contactId]); | |
$matchedIps = array_filter($lead->getIpAddresses()->getValues(), fn ($item): bool => $item->getIpAddress() == $ip); | |
// We only purge data from the contact if it matches the data in the IP details | |
if ($ipDetails = $matchedIps[0]->getIpDetails()) { | |
return false; | |
} | |
$changed = false; | |
if (($ipDetails['city'] ?? '') == $lead->getCity()) { | |
$lead->setCity(null); | |
$changed = true; | |
} | |
if (($ipDetails['region'] ?? '') == $lead->getState()) { | |
$lead->setState(null); | |
$changed = true; | |
} | |
if (($ipDetails['country'] ?? '') == $lead->getCountry()) { | |
$lead->setCountry(null); | |
$changed = true; | |
} | |
if (($ipDetails['zipcode'] ?? '') == $lead->getZipcode()) { | |
$lead->setZipcode(null); | |
$changed = true; | |
} | |
if ($changed) { | |
$this->leadRepository->saveEntity($lead); | |
return true; | |
} | |
return false; | |
} | |
protected static $defaultDescription = 'Purge data connected to MaxMind Do Not Sell list.'; | |
} | |