removed simplenews_mailjet_subscriptions, replaced it by features in custom module materio_mailjet

This commit is contained in:
2026-09-25 20:48:46 +02:00
parent ef699e9748
commit 89d68ac548
17 changed files with 272 additions and 156 deletions
@@ -0,0 +1,10 @@
materio_mailjet.settings:
type: config_object
label: 'Materio Mailjet settings'
mapping:
list_mapping:
type: sequence
label: 'Mailjet list ID to simplenews newsletter mapping'
sequence:
type: string
label: 'Newsletter ID'
@@ -1,8 +1,9 @@
name: Materio Mailjet
description: Add support for MailJet Webhooks.
description: Mailjet webhook support and simplenews to Mailjet list sync.
package: Materio
type: module
core_version_requirement: ^8.8 || ^9 || ^10.2 || ^11
dependencies:
- mailjet:mailjet
- simplenews:simplenews
@@ -0,0 +1,64 @@
<?php
/**
* @file
* Materio Mailjet: webhook support and simplenews-to-Mailjet list sync.
*/
use Drupal\simplenews\Entity\Subscriber;
/**
* Invoked if a subscriber is subscribed to a newsletter.
*
* Pushes the subscription to every Mailjet list mapped to the newsletter
* in materio_mailjet.settings.
*/
function materio_mailjet_simplenews_subscribe(Subscriber $subscriber, string $subscription) {
\Drupal::service('materio_mailjet.sync')->subscribeToNewsletter($subscription, (string) $subscriber->getMail());
}
/**
* Invoked if a subscriber is unsubscribed from a newsletter.
*
* Removes the subscription from every Mailjet list mapped to the newsletter.
*/
function materio_mailjet_simplenews_unsubscribe(Subscriber $subscriber, string $subscription) {
\Drupal::service('materio_mailjet.sync')->unsubscribeFromNewsletter($subscription, (string) $subscriber->getMail());
}
/**
* Act after a subscriber is updated.
*
* When the email changes, the old email is removed from the mapped lists
* and the new one is added.
*/
function materio_mailjet_simplenews_subscriber_update(Subscriber $subscriber) {
$new_mail = $subscriber->getMail();
$original_mail = $subscriber->original->getMail();
if ($new_mail === $original_mail) {
return;
}
$new_subscriptions = $subscriber->getSubscribedNewsletterIds();
$original_subscriptions = $subscriber->original->getSubscribedNewsletterIds();
$sync = \Drupal::service('materio_mailjet.sync');
$mapping = \Drupal::config('materio_mailjet.settings')->get('list_mapping') ?? [];
foreach ($mapping as $list_id => $newsletter_id) {
if (in_array($newsletter_id, $original_subscriptions)) {
$sync->unsubscribeFromList((string) $list_id, (string) $original_mail);
}
if (in_array($newsletter_id, $new_subscriptions)) {
$sync->subscribeToList((string) $list_id, (string) $new_mail);
}
}
}
/**
* Act after a subscriber has been deleted.
*
* Removes the email from every mapped Mailjet list.
*/
function materio_mailjet_simplenews_subscriber_delete(Subscriber $subscriber) {
\Drupal::service('materio_mailjet.sync')->unsubscribeFromAllLists((string) $subscriber->getMail());
}
@@ -0,0 +1,4 @@
services:
materio_mailjet.sync:
class: Drupal\materio_mailjet\MaterioMailjetSync
arguments: ['@config.factory', '@logger.factory', '@mailjet.factory']
@@ -4,7 +4,6 @@ namespace Drupal\materio_mailjet\Controller;
use Drupal\Core\Controller\ControllerBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\simplenews_mailjet_subscriptions\Entity\SimplenewsMailjetSubscriptionEntity;
use Drupal\simplenews\Entity\Subscriber;
use Drupal\simplenews\Subscription\SubscriptionManagerInterface;
@@ -80,24 +79,10 @@ class MaterioMailjetWebhookCallbackController extends ControllerBase {
$MjlistID = $event->mj_list_id;
$email = $event->email;
// find corresponding simplenews list from mailjet listID
$newsletter_id = null;
$entities = SimplenewsMailjetSubscriptionEntity::loadMultiple();
foreach ((array) $entities as $entity) {
$listID = (int)$entity->getID();
if ($listID === $MjlistID) {
$table = $entity->get('mapping_table');
if (is_array($table)) {
foreach ($table as $category) {
$newsletter_id = $category['simplenews_news'];
break;
}
}
break;
}
}
// \Drupal::logger('simplenews_mailjet_subscriptions')->info("Unsubscribe webhook triggered for listID: " . $listID . "and email: " . $email . ".");
// Find the simplenews newsletter mapped to this Mailjet list
// (mapping lives in materio_mailjet.settings).
$newsletter_id = \Drupal::service('materio_mailjet.sync')
->getNewsletterForListId((int) $MjlistID);
// get subscriber entity
// Load entities by their property values.
@@ -108,7 +93,7 @@ class MaterioMailjetWebhookCallbackController extends ControllerBase {
$subscriber = array_shift($subscribers);
if ($subscriber instanceof Subscriber) {
// $subscriber->unsubscribe($simplenews_news, 'mailjet');
$this->subscriptionManager->unsubscribe($subscriber->getMail(), $newsletter_id, FALSE, 'materio_mailjet');
$this->subscriptionManager->unsubscribe($subscriber->getMail(), $newsletter_id);
}
}
}
@@ -0,0 +1,181 @@
<?php
namespace Drupal\materio_mailjet;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\mailjet\MailjetFactory;
use Mailjet\Resources;
/**
* Syncs simplenews subscription events to Mailjet contact lists.
*
* Replacement for the unmaintained simplenews_mailjet_subscriptions module.
* Simplenews remains the consent store (owned data, provider-agnostic);
* Mailjet is a replaceable sender. The list mapping lives in
* materio_mailjet.settings, so switching provider only means replacing
* this adapter.
*/
class MaterioMailjetSync {
/**
* Mailjet client.
*
* @var \Mailjet\Client
*/
protected $mailjetClient;
/**
* Config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Logger.
*
* @var \Drupal\Core\Logger\LoggerChannelInterface
*/
protected $logger;
/**
* Constructs the sync service.
*/
public function __construct(ConfigFactoryInterface $config_factory, LoggerChannelFactoryInterface $logger_factory, MailjetFactory $mailjet_factory) {
$this->configFactory = $config_factory;
$this->logger = $logger_factory->get('materio_mailjet');
$this->mailjetClient = $mailjet_factory->create();
}
/**
* Returns the Mailjet list IDs mapped to a simplenews newsletter.
*
* @param string $newsletter_id
* The simplenews newsletter ID.
*
* @return array
* List of Mailjet list IDs (may be empty).
*/
public function getListIdsForNewsletter(string $newsletter_id): array {
$mapping = $this->configFactory->get('materio_mailjet.settings')->get('list_mapping') ?? [];
$list_ids = [];
foreach ($mapping as $list_id => $news_id) {
if ($news_id === $newsletter_id) {
$list_ids[] = (string) $list_id;
}
}
return $list_ids;
}
/**
* Returns the simplenews newsletter mapped to a Mailjet list ID.
*
* @param int $list_id
* The Mailjet list ID.
*
* @return string|null
* The newsletter ID, or NULL if unmapped.
*/
public function getNewsletterForListId(int $list_id): ?string {
$mapping = $this->configFactory->get('materio_mailjet.settings')->get('list_mapping') ?? [];
return $mapping[$list_id] ?? NULL;
}
/**
* Subscribes an email to every Mailjet list mapped to a newsletter.
*/
public function subscribeToNewsletter(string $newsletter_id, string $email): void {
if (empty($email)) {
return;
}
foreach ($this->getListIdsForNewsletter($newsletter_id) as $list_id) {
$this->subscribeToList($list_id, $email);
}
}
/**
* Unsubscribes an email from every Mailjet list mapped to a newsletter.
*/
public function unsubscribeFromNewsletter(string $newsletter_id, string $email): void {
if (empty($email)) {
return;
}
foreach ($this->getListIdsForNewsletter($newsletter_id) as $list_id) {
$this->unsubscribeFromList($list_id, $email);
}
}
/**
* Unsubscribes an email from every mapped Mailjet list.
*/
public function unsubscribeFromAllLists(string $email): void {
if (empty($email)) {
return;
}
$mapping = $this->configFactory->get('materio_mailjet.settings')->get('list_mapping') ?? [];
foreach (array_keys($mapping) as $list_id) {
$this->unsubscribeFromList((string) $list_id, $email);
}
}
/**
* Adds a contact to a Mailjet list (creates the contact if needed).
*/
public function subscribeToList(string $list_id, string $email): void {
if (!$this->ensureContact($email)) {
$this->logger->warning('@email does not exist and cannot be created on Mailjet.', ['@email' => $email]);
return;
}
$response = $this->mailjetClient->post(Resources::$ContactslistManagecontact, [
'id' => $list_id,
'body' => ['Action' => 'addnoforce', 'Email' => $email],
]);
if ($response->success()) {
$this->logger->notice('The Mailjet user @email subscribed to the @list list.', ['@email' => $email, '@list' => $list_id]);
}
else {
$this->logger->warning('Error while subscribing @email to the @list list.', ['@email' => $email, '@list' => $list_id]);
}
}
/**
* Removes a contact from a Mailjet list.
*/
public function unsubscribeFromList(string $list_id, string $email): void {
$response = $this->mailjetClient->post(Resources::$ContactslistManagecontact, [
'id' => $list_id,
'body' => ['Action' => 'remove', 'Email' => $email],
]);
if ($response->success()) {
$this->logger->notice('The Mailjet user @email was removed from the @list list.', ['@email' => $email, '@list' => $list_id]);
}
else {
$this->logger->warning('Error while removing @email from the @list list.', ['@email' => $email, '@list' => $list_id]);
}
}
/**
* Ensures a Mailjet contact exists for an email.
*
* @return bool
* TRUE if the contact exists or was created.
*/
protected function ensureContact(string $email): bool {
$response = $this->mailjetClient->get(Resources::$Contact, ['id' => urlencode($email)]);
if ($response->success()) {
return TRUE;
}
$response = $this->mailjetClient->post(Resources::$Contact, ['body' => ['Email' => $email]]);
if ($response->success()) {
return TRUE;
}
return FALSE;
}
}