Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dev/core#5482 Add in Unsubscribe Mode setting and field to allow for … #31259

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CRM/Admin/Form/Setting/Mail.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class CRM_Admin_Form_Setting_Mail extends CRM_Admin_Form_Setting {
'civimail_sync_interval' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
'replyTo' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
'civimail_unsubscribe_methods' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
'civimail_default_unsubscribe_mode' => CRM_Core_BAO_Setting::MAILING_PREFERENCES_NAME,
];

/**
Expand Down
2 changes: 2 additions & 0 deletions CRM/Mailing/BAO/Mailing.php
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ public static function create(array $params) {
'created_date' => date('YmdHis'),
'scheduled_date' => NULL,
'approval_date' => NULL,
'unsubscribe_mode' => Civi::settings()->get('civimail_default_unsubscribe_mode'),
];

// Get the default from email address, if not provided.
Expand Down Expand Up @@ -1931,6 +1932,7 @@ public static function self_hook_civicrm_pre(\Civi\Core\Event\PreEvent $event) {
$params['resubscribe_id'] ??= CRM_Mailing_PseudoConstant::defaultComponent('Resubscribe', '');
$params['unsubscribe_id'] ??= CRM_Mailing_PseudoConstant::defaultComponent('Unsubscribe', '');
$params['mailing_type'] ??= 'standalone';
$params['unsubscribe_mode'] ??= Civi::settings()->get('civimail_default_unsubscribe_mode');
}
if ($event->action === 'delete' && $event->id) {
// Delete all file attachments
Expand Down
1 change: 1 addition & 0 deletions CRM/Mailing/DAO/Mailing.php
JoeMurray marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* @property int|string|null $location_type_id
* @property string|null $email_selection_method
* @property string|null $language
* @property string $unsubscribe_mode
*/
class CRM_Mailing_DAO_Mailing extends CRM_Core_DAO_Base {
}
67 changes: 67 additions & 0 deletions CRM/Mailing/Page/OptOut.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
/*
+--------------------------------------------------------------------+
| Copyright CiviCRM LLC. All rights reserved. |
| |
| This work is published under the GNU AGPLv3 license with some |
| permitted exceptions and without any warranty. For full license |
| and copyright information, see https://civicrm.org/licensing |
+--------------------------------------------------------------------+
*/

/**
*
* @package CRM
* @copyright CiviCRM LLC https://civicrm.org/licensing
*/
class CRM_Mailing_Page_OptOut extends CRM_Core_Page {

/**
* Run page.
*
* This includes assigning smarty variables and other page processing.
*
* @return string
* @throws Exception
*/
public function run() {
$isOneClick = ($_SERVER['REQUEST_METHOD'] === 'POST' && CRM_Utils_Request::retrieve('List-Unsubscribe', 'String') === 'One-Click');
if ($isOneClick) {
$this->handleOneClick();
return NULL;
}

$wrapper = new CRM_Utils_Wrapper();
return $wrapper->run('CRM_Mailing_Form_OptOut', $this->_title);
}

/**
*
* Pre-condition: Validated the _job_id, _queue_id, _hash.
* Post-condition: Unsubscribed
*
* @link https://datatracker.ietf.org/doc/html/rfc8058
* @return void
*/
public function handleOneClick(): void {
$jobId = CRM_Utils_Request::retrieve('jid', 'Integer');
$queueId = CRM_Utils_Request::retrieve('qid', 'Integer');
$hash = CRM_Utils_Request::retrieve('h', 'String');

$q = CRM_Mailing_Event_BAO_MailingEventQueue::verify(NULL, $queueId, $hash);
if (!$q) {
CRM_Utils_System::sendResponse(
new \GuzzleHttp\Psr7\Response(400, [], ts("Invalid request: bad parameters"))
);
}

if (CRM_Mailing_Event_BAO_MailingEventUnsubscribe::unsub_from_domain($jobId, $queueId, $hash)) {
CRM_Mailing_Event_BAO_MailingEventUnsubscribe::send_unsub_response($queueId, NULL, TRUE, $jobId);
}

CRM_Utils_System::sendResponse(
new \GuzzleHttp\Psr7\Response(200, [], 'OK')
);
}

}
43 changes: 38 additions & 5 deletions CRM/Mailing/Service/ListUnsubscribe.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ public static function getMethods(): array {
];
}

public static function unsubscribeModes(): array {
return [
'unsubscribe' => ts('Unsubscribe'),
'opt-out' => ts('Opt Out'),
];
}

public static function getSubscribedEvents() {
return [
'&hook_civicrm_alterMailParams' => ['alterMailParams', 1000],
Expand All @@ -43,9 +50,7 @@ public function alterMailParams(&$params, $context = NULL): void {
}

$methods = Civi::settings()->get('civimail_unsubscribe_methods');
if ($methods === ['mailto']) {
return;
}
$mode = Civi::settings()->get('civimail_default_unsubscribe_mode');

$sep = preg_quote(Civi::settings()->get('verpSeparator'), ';');
$regex = ";^<mailto:[^>]*u{$sep}(\d+){$sep}(\d+){$sep}(\w*)@(.+)>$;";
Expand All @@ -54,6 +59,23 @@ public function alterMailParams(&$params, $context = NULL): void {
return;
}

$mailing_unsubscribe_mode = CRM_Core_DAO::singleValueQuery("SELECT m.unsubscribe_mode
FROM civicrm_mailing_event_queue mq
INNER JOIN civicrm_mailing m ON m.id = mq.mailing_id
WHERE mq.id = %1", [1 => [$m[2], 'Positive']]);
if ($mailing_unsubscribe_mode !== $mode) {
$mode = $mailing_unsubscribe_mode;
}
if ($methods === ['mailto']) {
JoeMurray marked this conversation as resolved.
Show resolved Hide resolved
if ($mode === 'unsubscribe') {
return;
}
else {
$params['List-Unsubscribe'] = $this->replaceUnsubscribeWithOptOut($params['List-Unsubscribe']);
return;
}
}

if ($this->urlFlags === NULL) {
$this->urlFlags = 'a';
if (in_array('oneclick', $methods) && empty(parse_url(CIVICRM_UF_BASEURL, PHP_URL_PORT))) {
Expand All @@ -65,10 +87,16 @@ public function alterMailParams(&$params, $context = NULL): void {

$listUnsubscribe = [];
if (in_array('mailto', $methods)) {
$listUnsubscribe[] = $params['List-Unsubscribe'];
$listUnsubscribe[] = ($mode === 'unsubscribe' ? $params['List-Unsubscribe'] : $this->replaceUnsubscribeWithOptOut($params['List-Unsubscribe']));
}
if (array_intersect(['http', 'oneclick'], $methods)) {
$listUnsubscribe[] = '<' . Civi::url('frontend://civicrm/mailing/unsubscribe', $this->urlFlags)->addQuery([
if ($mode === 'unsubscribe') {
$url = 'frontend://civicrm/mailing/unsubscribe';
}
else {
$url = 'frontend://civicrm/mailing/optout';
}
$listUnsubscribe[] = '<' . Civi::url($url, $this->urlFlags)->addQuery([
'reset' => 1,
'jid' => $m[1],
'qid' => $m[2],
Expand All @@ -83,4 +111,9 @@ public function alterMailParams(&$params, $context = NULL): void {
unset($params['List-Unsubscribe']);
}

private function replaceUnsubscribeWithOptOut($listUnsubscribeEmail): string {
$sep = Civi::settings()->get('verpSeparator');
return str_replace('u' . $sep, 'o' . $sep, $listUnsubscribeEmail);
}

}
2 changes: 1 addition & 1 deletion CRM/Mailing/xml/Menu/Mailing.xml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@
<item>
<path>civicrm/mailing/optout</path>
<title>Opt-out</title>
<page_callback>CRM_Mailing_Form_Optout</page_callback>
<page_callback>CRM_Mailing_Page_OptOut</page_callback>
<access_arguments>access CiviMail subscribe/unsubscribe pages</access_arguments>
<is_public>true</is_public>
<weight>650</weight>
Expand Down
7 changes: 7 additions & 0 deletions CRM/Upgrade/Incremental/php/FiveEighty.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ class CRM_Upgrade_Incremental_php_FiveEighty extends CRM_Upgrade_Incremental_Bas
*/
public function upgrade_5_80_alpha1($rev): void {
$this->addTask(ts('Upgrade DB to %1: SQL', [1 => $rev]), 'runSql', $rev);
$this->addTask('Add unsubscribe mode column to civicrm mailing', 'addColumn', 'civicrm_mailing', 'unsubscribe_mode', "VARCHAR(70) NOT NULL DEFAULT 'unsubscribe' COMMENT 'One Click Unsubscribe mode either unsubscribe or opt-out'");
$this->addTask('Populate Unsubscribe mode column on civicrm_mailing', 'populateUnsubscribeMode');
}

public static function populateUnsubscribeMode(): bool {
CRM_Core_DAO::executeQuery("UPDATE civicrm_mailing SET unsubscribe_mode = 'unsubscribe'");
return TRUE;
}

}
11 changes: 11 additions & 0 deletions ext/civi_mail/ang/crmMailing/BlockResponses.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,16 @@
<option value=""></option>
</select>
</div>
<div crm-ui-field="{name: 'subform.unsubscribe_mode', title: ts('Unsubscribe Mode')}">
<select
crm-ui-id="subform.unsubscribe_mode"
name="unsubscribe_mode"
crm-ui-select="{dropdownAutoWidth : true}"
ng-model="mailing.unsubscribe_mode"
required>
<option value="unsubscribe">{{:: ts('Unsubscribe') }}</option>
<option value="opt-out">{{:: ts('Opt Out') }}</option>
</select>
</div>
</div>
</div>
15 changes: 15 additions & 0 deletions schema/Mailing/Mailing.entityType.php
Original file line number Diff line number Diff line change
Expand Up @@ -609,5 +609,20 @@
'key_column' => 'name',
],
],
'unsubscribe_mode' => [
'title' => ts('One Click Unsubscribe Mode'),
'sql_type' => 'varchar(70)',
'input_type' => 'select',
'description' => ts('One Click Unsubscribe mode either unsubscribe or opt-out'),
'add' => '5.80',
'input_attrs' => [
'label' => ts('One Click Unsubscribe Mode'),
],
'pseudoconstant' => [
'callback' => ['CRM_Mailing_Service_ListUnsubscribe', 'unsubscribeModes'],
],
'default' => 'unsubscribe',
'required' => TRUE,
],
],
];
23 changes: 23 additions & 0 deletions settings/Mailing.setting.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,4 +447,27 @@
'description' => ts('Controls whether scheduled reminders will attempt to process smarty tokens.'),
'help_text' => NULL,
],
'civimail_default_unsubscribe_mode' => [
'group_name' => 'Mailing Preferences',
'group' => 'mailing',
'name' => 'civimail_default_unsubscribe_mode',
'type' => 'String',
'html_type' => 'select',
'html_attributes' => [
'class' => 'crm-select2',
],
'default' => 'unsubscribe',
'add' => '5.80',
'title' => ts('Default Unsubscribe Mode'),
'is_domain' => 1,
'is_contact' => 0,
'description' => ts("These methods will be offered to email clients for semi-automated unsubscribes. Support for each depends on the recipient's email client.") . $unsubLearnMore,
'help_text' => NULL,
'pseudoconstant' => [
'callback' => 'CRM_Mailing_Service_ListUnsubscribe::unsubscribeModes',
],
'settings_pages' => [
'mail' => ['weight' => 200],
],
],
];