Unsubscribe broken on multilingual sites -- may cause mass unsubscribes to all groups
Overview
On a multilingual website, accessing an unsubscribe page under different locales produces different results. When this page is not accessed under the expected locale, users will be asked to unsubscribe to all groups they are subscribed to.
Reproduction steps
- Create a multilingual instance of CivCRM (ex: en_CA, fr_CA)
- Access the groups page under a specific locale (ex: en_CA)
- Create 2 or more groups (ex: Mailing Lists) under that same specific locale (ex: en_CA)
- Subscribe a random user to both groups
- Send a CiviMail to one of these groups (sending a test email will do)
- On the email received, click on the unsubscribe link
- Access the unsubscribe link from a different locale, by changing the locale in the URL
Current behaviour
Accessing the page using the "wrong" locale will provide a list of all groups the user is currently subscribed to.
Submitting the form will unsubscribe the user to all listed groups.
Expected behaviour
The behaviour should be the same as accessing this page using the "correct" locale: only the target group should be shown on the list and users should only be unsubscribed from that group.
Environment information
- Browser: Firefox 73.0
- CiviCRM: 5.20.3
- PHP: 7.0.33-0+deb9u7
- CMS: Drupal 7.69
- Database: Ver 15.1 Distrib 10.1.44-MariaDB
- Web Server: nginx/1.14.0 (Ubuntu)
Comments
I have traced back the issue to CRM_Mailing_Event_BAO_Unsubscribe
's &unsub_from_mailing()
method.
It is caused by the value of $groupTableName
not matching the entity_table
column in the civicrm_mailing_group
table.
id | mailing_id | group_type | entity_table | entity_id | search_id | search_args |
---|---|---|---|---|---|---|
... | ... | ... | civicrm_group_en_CA | ... | NULL | NULL |
$groupTableName = `civicrm_group_fr_CA`
The value of $groupTableName
varies depending on the current locale, while the entity_table
points the localized version of civicrm_group
at the time of creation. The comparison should be agnostic, but it is not.
I temporarily fixed the issue by stripping the locale suffix from each references to civicrm_group_xx_XX
when comparing both values. It is a bit beyond my means, however, to understand how this should be correctly implemented. Does it make sense to localize the entity_table
in this context? What sort of fix can we afford without causing terrible side effects? What else is broken because of this situation? Is there a standard way to compare localized table names agnostically?
While I have just stumbled across this case, I would assume that it's been affecting multilingual site for a while now; and on top of that, these sites may suffer from unwanted unsubscriptions. I hope I'm wrong, because additional measures would be required to fix this.
This bug persist even after applying the patch found in this PR.