Skip to content

Commit

Permalink
Merged PR 60322: Add backend models to prevent email consent being en…
Browse files Browse the repository at this point in the history
…abled without consent text

## What's being changed

Enabling consent for email marketing in the connector admin. Merchants must now set both types of consent text (for customers and subscribers) before being allowed to turn on consent. In addition, merchants cannot clear consent text in either field if consent is enabled at any level.

## Why it's being changed

Consent text is a requirement for a valid consent record. Our SDK requires this and throws an error, which can disrupt both consent sync and fetching data from the API, as well as saves in connector/customer/index. So the way to prevent all of these is to prevent the text being empty in the first place. Resolves #629.

## How to review / test this change

- Go to Dotdigital > Marketing Consent
- Test enabling marketing consent with and without text in either textarea
- Confirm this works at website and store scope and with inherited values
- Set everything up so it's correct - then try deleting consent text in either textarea at any level (this should also not be possible)

Related work items: #216620
  • Loading branch information
sta1r committed Dec 2, 2024
1 parent 5582097 commit 1a76548
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 4 deletions.
93 changes: 93 additions & 0 deletions Model/Config/Backend/Consent/PreventConsentTextDeletion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

namespace Dotdigitalgroup\Email\Model\Config\Backend\Consent;

use Dotdigitalgroup\Email\Helper\Config;
use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Value;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Exception\ValidatorException;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;

class PreventConsentTextDeletion extends Value
{
/**
* @var RequestInterface
*/
private $request;

/**
* @param Context $context
* @param Registry $registry
* @param ScopeConfigInterface $config
* @param TypeListInterface $cacheTypeList
* @param RequestInterface $request
* @param AbstractResource|null $resource
* @param AbstractDb|null $resourceCollection
* @param array $data
*/
public function __construct(
Context $context,
Registry $registry,
ScopeConfigInterface $config,
TypeListInterface $cacheTypeList,
RequestInterface $request,
AbstractResource $resource = null,
AbstractDb $resourceCollection = null,
array $data = []
) {
$this->request = $request;
parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
}

/**
* Prevent consent text being deleted if Email marketing consent is enabled.
*
* @return Value
* @throws ValidatorException
*/
public function beforeSave()
{
$value = $this->getValue();
if (!empty($value)) {
return parent::beforeSave();
}

if ($this->isConsentEnabled()) {
throw new ValidatorException(
__(
'Opt-in consent text must be set if Email marketing consent is enabled.'
)
);
}

return parent::beforeSave();
}

/**
* Check if consent is enabled or inherited.
*
* @return bool
*/
private function isConsentEnabled(): bool
{
$inheritedIsConsentEnabled = $this->_config->isSetFlag(
Config::XML_PATH_CONSENT_EMAIL_ENABLED,
$this->getScope() ?: ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
$this->getScopeCode()
);

/** @var \Laminas\Http\Request $request */
$request = $this->request;
$groups = $request->getPost('groups');
$isConsentBeingEnabled = $groups['email']['fields']['enabled']['value'] ?? false;

return $inheritedIsConsentEnabled || $isConsentBeingEnabled;
}
}
115 changes: 115 additions & 0 deletions Model/Config/Backend/Consent/RequireConsentText.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<?php

declare(strict_types=1);

namespace Dotdigitalgroup\Email\Model\Config\Backend\Consent;

use Dotdigitalgroup\Email\Helper\Config;
use Magento\Framework\App\Cache\TypeListInterface;
use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\App\Config\Value;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Exception\ValidatorException;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;

class RequireConsentText extends Value
{
/**
* @var RequestInterface
*/
private $request;

/**
* @param Context $context
* @param Registry $registry
* @param ScopeConfigInterface $config
* @param TypeListInterface $cacheTypeList
* @param RequestInterface $request
* @param AbstractResource|null $resource
* @param AbstractDb|null $resourceCollection
* @param array $data
*/
public function __construct(
Context $context,
Registry $registry,
ScopeConfigInterface $config,
TypeListInterface $cacheTypeList,
RequestInterface $request,
AbstractResource $resource = null,
AbstractDb $resourceCollection = null,
array $data = []
) {
$this->request = $request;
parent::__construct($context, $registry, $config, $cacheTypeList, $resource, $resourceCollection, $data);
}

/**
* Require consent text (both customer and subscriber texts) before enabling Email marketing consent.
*
* @return Value
* @throws ValidatorException
*/
public function beforeSave()
{
$value = $this->getValue();
if ($value == "0") {
return parent::beforeSave();
}

if ($this->isConsentCustomerTextSetOrInherited() === false ||
$this->isConsentSubscriberTextSetOrInherited() === false) {
throw new ValidatorException(
__(
'Please set all required opt-in consent text before enabling Email marketing consent.'
)
);
}

return parent::beforeSave();
}

/**
* Check if consent customer text is set or inherited.
*
* @return bool
*/
private function isConsentCustomerTextSetOrInherited(): bool
{
$inheritedConsentCustomerText = $this->_config->getValue(
Config::XML_PATH_CONSENT_CUSTOMER_TEXT,
$this->getScope() ?: ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
$this->getScopeCode()
);

/** @var \Laminas\Http\Request $request */
$request = $this->request;
$groups = $request->getPost('groups');
$consentCustomerText = $groups['email']['fields']['text_newsletter_registration_checkout']['value'] ?? null;

return !empty($inheritedConsentCustomerText) || !empty($consentCustomerText);
}

/**
* Check if consent subscriber text is set or inherited.
*
* @return bool
*/
private function isConsentSubscriberTextSetOrInherited(): bool
{
$inheritedConsentSubscriberText = $this->_config->getValue(
Config::XML_PATH_CONSENT_SUBSCRIBER_TEXT,
$this->getScope() ?: ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
$this->getScopeCode()
);

/** @var \Laminas\Http\Request $request */
$request = $this->request;
$groups = $request->getPost('groups');
$consentSubscriberText = $groups['email']['fields']['text_newsletter_signup_form']['value'] ?? null;

return !empty($inheritedConsentSubscriberText) || !empty($consentSubscriberText);
}
}
16 changes: 12 additions & 4 deletions etc/adminhtml/system.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1338,19 +1338,27 @@
<resource>Dotdigitalgroup_Email::config</resource>
<group id="email" translate="label" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
<label>Email</label>
<field id="enabled" translate="label" sortOrder="10" type="select" showInStore="1"
showInWebsite="1" showInDefault="1">
<field id="enabled" translate="label" sortOrder="10" type="select" showInStore="1" showInWebsite="1" showInDefault="1">
<label>Import subscribers with consent</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<backend_model>Dotdigitalgroup\Email\Model\Config\Backend\Consent\RequireConsentText</backend_model>
<comment><![CDATA[Enable this to record consent when customers subscribe or opt-in anywhere on your store.]]></comment>
</field>
<field id="text_newsletter_registration_checkout" translate="label" sortOrder="20" type="textarea" showInWebsite="1" showInStore="1" showInDefault="1">
<label>Opt-in consent text (checkout, account)</label>
<comment><![CDATA[Enter the consent text you want to record against contacts that opt-in from a page with a URL that includes checkout/ or customer/account/.]]></comment>
<backend_model>Dotdigitalgroup\Email\Model\Config\Backend\Consent\PreventConsentTextDeletion</backend_model>
<comment><![CDATA[[Required] Enter the consent text you want to record against contacts that opt-in from a page with a URL that includes checkout/ or customer/account/.]]></comment>
<depends>
<field id="enabled">1</field>
</depends>
</field>
<field id="text_newsletter_signup_form" translate="label" sortOrder="20" type="textarea" showInWebsite="1" showInStore="1" showInDefault="1">
<label>Opt-in consent text (other pages)</label>
<comment><![CDATA[Enter the consent text you want to record against contacts that opt-in from any page that doesn't have a URL that includes checkout/ or customer/account/.]]></comment>
<backend_model>Dotdigitalgroup\Email\Model\Config\Backend\Consent\PreventConsentTextDeletion</backend_model>
<comment><![CDATA[[Required] Enter the consent text you want to record against contacts that opt-in from any page that doesn't have a URL that includes checkout/ or customer/account/.]]></comment>
<depends>
<field id="enabled">1</field>
</depends>
</field>
</group>
</section>
Expand Down

0 comments on commit 1a76548

Please sign in to comment.