From 3e68c0f810b28da4c5de84e321802bb71b96a730 Mon Sep 17 00:00:00 2001 From: Luca Rath-Heel Date: Mon, 24 Jan 2022 11:03:13 +0100 Subject: [PATCH] Add sendinblue integration (#310) --- Command/FormGeneratorCommand.php | 3 - DependencyInjection/Configuration.php | 1 + DependencyInjection/SuluFormExtension.php | 9 ++ Dynamic/Helper/SendinblueListSelect.php | 55 +++++++++ .../Helper/SendinblueMailTemplateSelect.php | 59 +++++++++ Dynamic/Types/SendinblueType.php | 53 ++++++++ Event/SendinblueListSubscriber.php | 114 ++++++++++++++++++ Repository/FormRepository.php | 1 - .../config/form-fields/field_sendinblue.xml | 31 +++++ Resources/config/type_sendinblue.xml | 25 ++++ Resources/doc/index.md | 1 + Resources/doc/sendinblue.md | 38 ++++++ Resources/translations/admin.de.json | 6 + Resources/translations/admin.en.json | 6 + composer.json | 20 +-- 15 files changed, 409 insertions(+), 13 deletions(-) create mode 100644 Dynamic/Helper/SendinblueListSelect.php create mode 100644 Dynamic/Helper/SendinblueMailTemplateSelect.php create mode 100644 Dynamic/Types/SendinblueType.php create mode 100644 Event/SendinblueListSubscriber.php create mode 100644 Resources/config/form-fields/field_sendinblue.xml create mode 100644 Resources/config/type_sendinblue.xml create mode 100644 Resources/doc/sendinblue.md diff --git a/Command/FormGeneratorCommand.php b/Command/FormGeneratorCommand.php index ebda48fe..02ec992c 100644 --- a/Command/FormGeneratorCommand.php +++ b/Command/FormGeneratorCommand.php @@ -267,9 +267,6 @@ private function loadTestForm(): ?Form } } - /** - * @return string - */ private function getChoices(): string { return diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php index dcbb82e6..c03f8573 100644 --- a/DependencyInjection/Configuration.php +++ b/DependencyInjection/Configuration.php @@ -36,6 +36,7 @@ public function getConfigTreeBuilder() $rootNode = $treeBuilder->getRootNode(); $rootNode->children() + ->scalarNode('sendinblue_api_key')->defaultValue(null)->end() ->scalarNode('mailchimp_api_key')->defaultValue(null)->end() ->scalarNode('mailchimp_subscribe_status')->defaultValue('subscribed')->end() ->enumNode('media_collection_strategy') diff --git a/DependencyInjection/SuluFormExtension.php b/DependencyInjection/SuluFormExtension.php index 04567a00..0f5f382e 100644 --- a/DependencyInjection/SuluFormExtension.php +++ b/DependencyInjection/SuluFormExtension.php @@ -148,6 +148,7 @@ public function load(array $configs, ContainerBuilder $container): void $container->setParameter('sulu_form.ajax_templates', $config['ajax_templates']); $container->setParameter('sulu_form.dynamic_widths', $config['dynamic_widths']); $container->setParameter('sulu_form.dynamic_auto_title', $config['dynamic_auto_title']); + $container->setParameter('sulu_form.sendinblue_api_key', $config['sendinblue_api_key']); $container->setParameter('sulu_form.mailchimp_api_key', $config['mailchimp_api_key']); $container->setParameter('sulu_form.mailchimp_subscribe_status', $config['mailchimp_subscribe_status']); $container->setParameter('sulu_form.dynamic_lists.config', $config['dynamic_lists']); @@ -188,6 +189,14 @@ public function load(array $configs, ContainerBuilder $container): void $loader->load('types.xml'); $loader->load('title-providers.xml'); + if ($config['sendinblue_api_key']) { + if (!class_exists(\SendinBlue\Client\Configuration::class)) { + throw new \LogicException('You need to install the "sendinblue/api-v3-sdk" package to use the sendinblue type.'); + } + + $loader->load('type_sendinblue.xml'); + } + if ($config['mailchimp_api_key']) { if (!class_exists(\DrewM\MailChimp\MailChimp::class)) { throw new \LogicException('You need to install the "drewm/mailchimp-api" package to use the mailchimp type.'); diff --git a/Dynamic/Helper/SendinblueListSelect.php b/Dynamic/Helper/SendinblueListSelect.php new file mode 100644 index 00000000..cff060b3 --- /dev/null +++ b/Dynamic/Helper/SendinblueListSelect.php @@ -0,0 +1,55 @@ +setApiKey('api-key', $apiKey); + + $this->contactsApi = new ContactsApi(null, $config); + } + + /** + * Returns array of Sendinblue lists of given account defined by the API key. + * + * @return mixed[] + */ + public function getValues(): array + { + $lists = []; + + $listObjects = $this->contactsApi->getLists()->getLists(); + foreach ($listObjects as $list) { + $lists[] = [ + 'name' => $list['id'], + 'title' => $list['name'], + ]; + } + + return $lists; + } +} diff --git a/Dynamic/Helper/SendinblueMailTemplateSelect.php b/Dynamic/Helper/SendinblueMailTemplateSelect.php new file mode 100644 index 00000000..c4a4f950 --- /dev/null +++ b/Dynamic/Helper/SendinblueMailTemplateSelect.php @@ -0,0 +1,59 @@ +setApiKey('api-key', $apiKey); + + $this->transactionalEmailsApi = new TransactionalEmailsApi(null, $config); + } + + /** + * Returns array of Sendinblue mail templates of given account defined by the API key. + * + * @return mixed[] + */ + public function getValues(): array + { + $mailTemplates = []; + + $mailTemplateObjects = $this->transactionalEmailsApi->getSmtpTemplates('true')->getTemplates(); + foreach ($mailTemplateObjects as $template) { + if (($template['tag'] ?? null) !== 'optin') { + continue; + } + + $mailTemplates[] = [ + 'name' => $template['id'], + 'title' => $template['name'], + ]; + } + + return $mailTemplates; + } +} diff --git a/Dynamic/Types/SendinblueType.php b/Dynamic/Types/SendinblueType.php new file mode 100644 index 00000000..ad9a3a44 --- /dev/null +++ b/Dynamic/Types/SendinblueType.php @@ -0,0 +1,53 @@ +add($field->getKey(), $type, $options); + } + + /** + * {@inheritdoc} + */ + public function getDefaultValue(FormField $field, string $locale) + { + return $field->getTranslation($locale)->getDefaultValue(); + } +} diff --git a/Event/SendinblueListSubscriber.php b/Event/SendinblueListSubscriber.php new file mode 100644 index 00000000..a0d9e2c8 --- /dev/null +++ b/Event/SendinblueListSubscriber.php @@ -0,0 +1,114 @@ +requestStack = $requestStack; + + $config = new Configuration(); + $config->setApiKey('api-key', $apiKey); + + $this->contactsApi = new ContactsApi(null, $config); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + FormSavePostEvent::NAME => 'listSubscribe', + ]; + } + + public function listSubscribe(FormSavePostEvent $event): void + { + $dynamic = $event->getData(); + $request = $this->requestStack->getCurrentRequest(); + + if (!$dynamic instanceof Dynamic) { + return; + } + + if (!$request) { + return; + } + + $form = $dynamic->getForm()->serializeForLocale($dynamic->getLocale(), $dynamic); + + $email = ''; + $firstName = ''; + $lastName = ''; + $redirectionUrl = $request->getUriForPath('') . '?subscribe=true'; + $listIdsByMailTemplate = []; + + foreach ($form['fields'] as $field) { + if ('firstName' === $field['type'] && !$firstName) { + $firstName = $field['value']; + } elseif ('lastName' === $field['type'] && !$lastName) { + $lastName = $field['value']; + } elseif ('email' === $field['type'] && !$email) { + $email = $field['value']; + } elseif ('sendinblue' == $field['type'] && $field['value']) { + $mailTemplateId = $field['options']['mailTemplateId'] ?? null; + $listId = $field['options']['listId'] ?? null; + + if (!$mailTemplateId || !$listId) { + continue; + } + + $listIdsByMailTemplate[$mailTemplateId][] = $listId; + } + } + + if ($email && count($listIdsByMailTemplate) > 0) { + foreach ($listIdsByMailTemplate as $mailTemplateId => $listIds) { + $createDoiContact = new CreateDoiContact([ + 'email' => $email, + 'templateId' => $mailTemplateId, + 'includeListIds' => $listIds, + 'redirectionUrl' => $redirectionUrl, + 'attributes' => [ + 'FIRST_NAME' => $firstName, + 'LAST_NAME' => $firstName, + ], + ]); + + $this->contactsApi->createDoiContact($createDoiContact); + } + } + } +} diff --git a/Repository/FormRepository.php b/Repository/FormRepository.php index 8215e0b1..257b813d 100644 --- a/Repository/FormRepository.php +++ b/Repository/FormRepository.php @@ -16,7 +16,6 @@ use Sulu\Bundle\FormBundle\Entity\Form; /** - * * @template-extends EntityRepository
*/ class FormRepository extends EntityRepository diff --git a/Resources/config/form-fields/field_sendinblue.xml b/Resources/config/form-fields/field_sendinblue.xml new file mode 100644 index 00000000..efbf9652 --- /dev/null +++ b/Resources/config/form-fields/field_sendinblue.xml @@ -0,0 +1,31 @@ + + + + + sulu_form.sendinblue_list + + + + + + + + sulu_form.sendinblue_mail_template + + + + + + diff --git a/Resources/config/type_sendinblue.xml b/Resources/config/type_sendinblue.xml new file mode 100644 index 00000000..5ec0ed7d --- /dev/null +++ b/Resources/config/type_sendinblue.xml @@ -0,0 +1,25 @@ + + + + + + + %sulu_form.sendinblue_api_key% + + + + + + + + + %sulu_form.sendinblue_api_key% + + + + %sulu_form.sendinblue_api_key% + + + diff --git a/Resources/doc/index.md b/Resources/doc/index.md index 7006c537..7ebe3a34 100644 --- a/Resources/doc/index.md +++ b/Resources/doc/index.md @@ -89,6 +89,7 @@ Make sure you've set the correct permissions in the Sulu backend for this bundle ## Additional form fields - [Mailchimp](mailchimp.md "Mailchimp Form Field") +- [Sendinblue](sendinblue.md "Sendinblue Form Field") - [Recaptcha](recaptcha.md "Recaptcha Form Field") - [Dropzone](dropzone.md "Dropzone Form Field") diff --git a/Resources/doc/sendinblue.md b/Resources/doc/sendinblue.md new file mode 100644 index 00000000..efd5a8a5 --- /dev/null +++ b/Resources/doc/sendinblue.md @@ -0,0 +1,38 @@ +# Sendinblue Form Field + +The following is showing an example how you can use the Sendinblue form field to add customers to your Sendinblue lists. + +## Installation + +First of all you need to install the sendinblue php sdk, otherwise the Sendinblue field type won't be available. + +```json +{ + "require": { + "sendinblue/api-v3-sdk": "^8.0" + } +} +``` + +or + +```bash +composer require sendinblue/api-v3-sdk:"^8.0" +``` + +## Config + +Add the following config to `config/packages/sulu_form.yaml`: + +```yml +sulu_form: + sendinblue_api_key: "" +``` + +It is recommended to store the api key as environment variable see [Symfony Docs](https://symfony.com/doc/4.4/configuration.html#configuration-environments). + +## Why is there still no Sendinblue field type? + +1. Did you install the library? +2. Did you add an api key? +3. Did you create a list and an `optin`-tagged mail template that can be shown in the field type? diff --git a/Resources/translations/admin.de.json b/Resources/translations/admin.de.json index 0cca92af..7cf69621 100644 --- a/Resources/translations/admin.de.json +++ b/Resources/translations/admin.de.json @@ -67,6 +67,7 @@ "sulu_form.type.radiobuttons": "Radio Buttons", "sulu_form.type.recaptcha": "Captcha", "sulu_form.type.mailchimp": "Mailchimp", + "sulu_form.type.sendinblue": "Sendinblue", "sulu_form.width.full": "Voll", "sulu_form.width.half": "Halb", "sulu_form.width.one_quarter": "Ein Viertel", @@ -76,6 +77,11 @@ "sulu_form.width.one_sixth": "Ein Sechstel", "sulu_form.width.five_sixths": "Fünf Sechstel", "sulu_form.mailchimp_list": "Mailchimp Liste", + "sulu_form.sendinblue_list": "Sendinblue Liste", + "sulu_form.sendinblue_mail_template": "Sendinblue Mail Vorlage", + "sulu_form.sendinblue_redirection_url": "Weiterleitungs-URL", + "sulu_form.sendinblue_attribute_name_first_name": "Attribut-Name [Vorname]", + "sulu_form.sendinblue_attribute_name_last_name": "Attribut-Name [Nachname]", "sulu_form.salutation_mr": "Herr", "sulu_form.salutation_ms": "Frau", "sulu_form.single_form_selection.no_form_selected": "Kein Formular ausgewählt", diff --git a/Resources/translations/admin.en.json b/Resources/translations/admin.en.json index 298ce36b..3fd3b74d 100644 --- a/Resources/translations/admin.en.json +++ b/Resources/translations/admin.en.json @@ -67,6 +67,7 @@ "sulu_form.type.radiobuttons": "Radio Buttons", "sulu_form.type.recaptcha": "Captcha", "sulu_form.type.mailchimp": "Mailchimp", + "sulu_form.type.sendinblue": "Sendinblue", "sulu_form.width.full": "Full", "sulu_form.width.half": "Half", "sulu_form.width.one_quarter": "One quarter", @@ -76,6 +77,11 @@ "sulu_form.width.one_sixth": "One sixth", "sulu_form.width.five_sixths": "Five sixths", "sulu_form.mailchimp_list": "Mailchimp List", + "sulu_form.sendinblue_list": "Sendinblue list", + "sulu_form.sendinblue_mail_template": "Sendinblue mail template", + "sulu_form.sendinblue_redirection_url": "Redirection url", + "sulu_form.sendinblue_attribute_name_first_name": "Attribute name [First name]", + "sulu_form.sendinblue_attribute_name_last_name": "Attribute name [Last name]", "sulu_form.salutation_mr": "Mr.", "sulu_form.salutation_ms": "Ms.", "sulu_form.single_form_selection.no_form_selected": "No form selected", diff --git a/composer.json b/composer.json index aec65ff7..f617a8df 100644 --- a/composer.json +++ b/composer.json @@ -42,28 +42,30 @@ "friendsofphp/php-cs-fixer": "^2.17", "handcraftedinthealps/zendsearch": "^2.0", "jackalope/jackalope-doctrine-dbal": "^1.3.2", + "jangregor/phpstan-prophecy": "^1.0", "massive/search-bundle": "^2.0", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-doctrine": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-symfony": "^1.0", "phpunit/phpunit": "^8.0", - "symfony/mime": "^4.4 || ^5.0", - "symfony/mailer": "^4.4 || ^5.0", + "sendinblue/api-v3-sdk": "^8.0", "symfony/browser-kit": "^4.4 || ^5.0", "symfony/dotenv": "^4.4 || ^5.0", + "symfony/mailer": "^4.4 || ^5.0", + "symfony/mime": "^4.4 || ^5.0", "symfony/monolog-bundle": "^3.1", "symfony/stopwatch": "^4.4 || ^5.0", "symfony/var-dumper": "^4.4 || ^5.0", - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-doctrine": "^1.0", - "phpstan/phpstan-phpunit": "^1.0", - "phpstan/phpstan-symfony": "^1.0", - "thecodingmachine/phpstan-strict-rules": "^1.0", - "jangregor/phpstan-prophecy": "^1.0" + "thecodingmachine/phpstan-strict-rules": "^1.0" }, "replace": { "sulu/sulu-form-bundle": "self.version" }, "suggest": { "excelwebzone/recaptcha-bundle": "Allows to add recaptcha to any dynamic form", - "drewm/mailchimp-api": "Allows to add a newsletter subscription over mailchimp to any dynamic form" + "drewm/mailchimp-api": "Allows to add a newsletter subscription over mailchimp to any dynamic form", + "sendinblue/api-v3-sdk": "Allows to add a newsletter subscription over sendinblue to any dynamic form" }, "autoload": { "psr-4": {