diff --git a/composer.json b/composer.json index d111fb78..468f1427 100644 --- a/composer.json +++ b/composer.json @@ -127,7 +127,6 @@ "drupal/select_translation": "2.0.0-alpha5", "drupal/session_limit": "^2.0@beta", "drupal/simplenews": "^4.0", - "drupal/simplenews_mailjet_subscriptions": "^1.3", "drupal/social_media_links": "^2.8", "drupal/straw": "^2.0@beta", "drupal/string_translation_ui": "2.0.x-dev", @@ -254,9 +253,6 @@ "weitzman/drupal-test-traits": { "Define void return type for setUp() and tearDown() methods https://gitlab.com/weitzman/drupal-test-traits/-/issues/78":"./patches/Definevoidreturntype2.patch" }, - "drupal/simplenews_mailjet_subscriptions": { - "using mailjet module https://www.drupal.org/project/simplenews_mailjet_subscriptions/issues/3318780":"./patches/simplenews_mailjet_subscriptions__usingmailjetmodule#4.patch" - }, "drupal/graphql": { "invalid translation language https://github.com/drupal-graphql/graphql/pull/1176": "./patches/graphql-fix-invalid-translation-language-dev.patch" }, diff --git a/composer.lock b/composer.lock index 37250791..6254b19d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "05f35e541be129d9f1b9f6ecc1f2bf52", + "content-hash": "3e5547d3554eb3a78de6743962b8f123", "packages": [ { "name": "asm89/stack-cors", @@ -9863,79 +9863,6 @@ "source": "https://git.drupalcode.org/project/simplenews" } }, - { - "name": "drupal/simplenews_mailjet_subscriptions", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://git.drupalcode.org/project/simplenews_mailjet_subscriptions.git", - "reference": "8.x-1.3" - }, - "dist": { - "type": "zip", - "url": "https://ftp.drupal.org/files/projects/simplenews_mailjet_subscriptions-8.x-1.3.zip", - "reference": "8.x-1.3", - "shasum": "68f04b89512bcf413532f918cdaf36621a70b20b" - }, - "require": { - "drupal/core": "^8 || ^9 || ^10", - "drupal/simplenews": "*" - }, - "type": "drupal-module", - "extra": { - "drupal": { - "version": "8.x-1.3", - "datestamp": "1663058947", - "security-coverage": { - "status": "covered", - "message": "Covered by Drupal's security advisory policy" - } - } - }, - "notification-url": "https://packages.drupal.org/8/downloads", - "license": [ - "GPL-2.0-or-later" - ], - "authors": [ - { - "name": "dgaspara", - "homepage": "https://www.drupal.org/user/3557179" - }, - { - "name": "joaomarques736", - "homepage": "https://www.drupal.org/user/3557181" - }, - { - "name": "nsalves", - "homepage": "https://www.drupal.org/user/3557178" - }, - { - "name": "pmaiacar", - "homepage": "https://www.drupal.org/user/3575425" - }, - { - "name": "ricardotenreiro", - "homepage": "https://www.drupal.org/user/213132" - }, - { - "name": "sara_asb", - "homepage": "https://www.drupal.org/user/3673657" - }, - { - "name": "tiagopastor", - "homepage": "https://www.drupal.org/user/3656519" - }, - { - "name": "tmiguelv", - "homepage": "https://www.drupal.org/user/3612421" - } - ], - "description": "Allows you to manage the subscriptions in a mailjet account .", - "homepage": "https://www.drupal.org/project/simplenews_mailjet_subscriptions", - "support": { - "source": "https://git.drupalcode.org/project/simplenews_mailjet_subscriptions" - } - }, { "name": "drupal/smart_trim", "version": "2.3.1", diff --git a/config/devel/devel.settings.yml b/config/devel/devel.settings.yml new file mode 100644 index 00000000..f434c82b --- /dev/null +++ b/config/devel/devel.settings.yml @@ -0,0 +1 @@ +devel_dumper: var_dumper diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index 38b87164..413c7721 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -177,7 +177,6 @@ module: serialization: 0 session_limit: 0 simplenews: 0 - simplenews_mailjet_subscriptions: 0 smart_trim: 0 smtp: 0 social_media_links: 0 diff --git a/config/sync/materio_mailjet.settings.yml b/config/sync/materio_mailjet.settings.yml new file mode 100644 index 00000000..b6571490 --- /dev/null +++ b/config/sync/materio_mailjet.settings.yml @@ -0,0 +1,3 @@ +list_mapping: + 19125: ze_daily_materio_ + 22839: materio_newsletter diff --git a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.19125.yml b/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.19125.yml deleted file mode 100644 index 755ee6e3..00000000 --- a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.19125.yml +++ /dev/null @@ -1,11 +0,0 @@ -uuid: 89d71508-f9fd-47ad-b712-a2902f867792 -langcode: fr -status: true -dependencies: { } -id: '19125' -title: ZeDaily -mapping_table: - - - simplenews_news: ze_daily_materio_ - contact_property: simplenews - newsletter_property_name: ZeDaily diff --git a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.22839.yml b/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.22839.yml deleted file mode 100644 index 67cca140..00000000 --- a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.22839.yml +++ /dev/null @@ -1,11 +0,0 @@ -uuid: 9a82ce3e-d5b1-4226-9e70-e4f2d28334ca -langcode: fr -status: true -dependencies: { } -id: '22839' -title: "materio' newsletter" -mapping_table: - - - simplenews_news: materio_newsletter - contact_property: simplenews - newsletter_property_name: Newsletter diff --git a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24787.yml b/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24787.yml deleted file mode 100644 index 0f57e578..00000000 --- a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24787.yml +++ /dev/null @@ -1,11 +0,0 @@ -uuid: 494ab8bc-97f4-4e87-a9ac-876911d4d3a3 -langcode: fr -status: true -dependencies: { } -id: '24787' -title: Compagnie -mapping_table: - - - simplenews_news: companies - contact_property: simplenews - newsletter_property_name: Companies diff --git a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24788.yml b/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24788.yml deleted file mode 100644 index cd52fca6..00000000 --- a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.24788.yml +++ /dev/null @@ -1,11 +0,0 @@ -uuid: 22d88999-43ab-4e59-8deb-02760e11f7ad -langcode: fr -status: true -dependencies: { } -id: '24788' -title: Test -mapping_table: - - - simplenews_news: test - contact_property: simplenews - newsletter_property_name: Test diff --git a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.26308.yml b/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.26308.yml deleted file mode 100644 index d663de69..00000000 --- a/config/sync/simplenews_mailjet_subscriptions.simplenews_mailjet_subsciption.26308.yml +++ /dev/null @@ -1,11 +0,0 @@ -uuid: 87685e0e-fd2a-46af-a915-d0da2d947f91 -langcode: en -status: true -dependencies: { } -id: '26308' -title: Membres -mapping_table: - - - simplenews_news: membres - contact_property: simplenews - newsletter_property_name: Membres diff --git a/config/sync/views.view.simplenews_subscribers.yml b/config/sync/views.view.simplenews_subscribers.yml index 865ea143..1a6f99dc 100644 --- a/config/sync/views.view.simplenews_subscribers.yml +++ b/config/sync/views.view.simplenews_subscribers.yml @@ -1380,10 +1380,10 @@ display: automatic_download: true export_method: batch export_batch_size: 1000 + export_filesystem: private custom_redirect_path: false redirect_to_display: page_1 include_query_params: true - export_filesystem: private cache_metadata: max-age: -1 contexts: diff --git a/web/modules/custom/materio_mailjet/config/schema/materio_mailjet.schema.yml b/web/modules/custom/materio_mailjet/config/schema/materio_mailjet.schema.yml new file mode 100644 index 00000000..18bb46b7 --- /dev/null +++ b/web/modules/custom/materio_mailjet/config/schema/materio_mailjet.schema.yml @@ -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' diff --git a/web/modules/custom/materio_mailjet/materio_mailjet.info.yml b/web/modules/custom/materio_mailjet/materio_mailjet.info.yml index 1ea6fab7..7f754b15 100644 --- a/web/modules/custom/materio_mailjet/materio_mailjet.info.yml +++ b/web/modules/custom/materio_mailjet/materio_mailjet.info.yml @@ -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 diff --git a/web/modules/custom/materio_mailjet/materio_mailjet.module b/web/modules/custom/materio_mailjet/materio_mailjet.module new file mode 100644 index 00000000..b8f6f82f --- /dev/null +++ b/web/modules/custom/materio_mailjet/materio_mailjet.module @@ -0,0 +1,64 @@ +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()); +} diff --git a/web/modules/custom/materio_mailjet/materio_mailjet.services.yml b/web/modules/custom/materio_mailjet/materio_mailjet.services.yml new file mode 100644 index 00000000..11bd92bf --- /dev/null +++ b/web/modules/custom/materio_mailjet/materio_mailjet.services.yml @@ -0,0 +1,4 @@ +services: + materio_mailjet.sync: + class: Drupal\materio_mailjet\MaterioMailjetSync + arguments: ['@config.factory', '@logger.factory', '@mailjet.factory'] diff --git a/web/modules/custom/materio_mailjet/src/Controller/MaterioMailjetWebhookCallbackController.php b/web/modules/custom/materio_mailjet/src/Controller/MaterioMailjetWebhookCallbackController.php index c45ddfa6..d40fe108 100644 --- a/web/modules/custom/materio_mailjet/src/Controller/MaterioMailjetWebhookCallbackController.php +++ b/web/modules/custom/materio_mailjet/src/Controller/MaterioMailjetWebhookCallbackController.php @@ -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); } } } diff --git a/web/modules/custom/materio_mailjet/src/MaterioMailjetSync.php b/web/modules/custom/materio_mailjet/src/MaterioMailjetSync.php new file mode 100644 index 00000000..8a4284f0 --- /dev/null +++ b/web/modules/custom/materio_mailjet/src/MaterioMailjetSync.php @@ -0,0 +1,181 @@ +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; + } + +}