Problem/Motivation
See #3152102: Anonymous user can alter fields of any subscriber and #3238247: Major confusion for subscriptions during user registration.
Both bugs come from a common problem: the need to store unconfirmed subscription information whilst waiting for the subscriber to confirm. The unconfirmed data must not overwrite the live data. The Subscriber entity already has two mechanisms that attempt to address this:
- The changes field stores unconfirmed subscribe requests.
- The subscriptions field includes a status with 3 states, one of which is SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED.
Both are flawed for the same reasons: they store only the subscriptions, and not the supplemental subscriber field data; they can only store a single pending change; there is no unique identifier to correlate data with the user event that triggered it.
Proposed resolution
Summary:
- Store each unconfirmed subscription in a separate subscriber object
- Add a new field to indicate the unconfirmed status and remove
SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED - Remove
Subscriber::getChanges()andSubscriber::setChanges() - When the subscription is confirmed, search for any existing subscription and merge them together
- These changes are not back-compatible so require a new 4.x branch.
- Also see all the "changes" sections below.
Details:
- In
SubscriptionManager, removesendConfirmations(),destruct(),addConfirmation(),requiresConfirmation($uid). - In
ConfirmationController::confirmCombined(), delete the block relating to no changes available. Remove the changes from the hash, and same insimplenews_tokens(). In the ‘else’ block around line 103, call$subscriber->setStatus(SubscriberInterface::ACTIVE)and same inConfirmMultiForm::submitForm(). - In
SubscriptionsBlockForm ::submitExtra(), if the subscriber isn’t confirmed then callsendConfirmation(). Remove the call to subscription manager, instead save directly to the subscriber. - Modify the unique field constraint on mail to use a new custom constraint that requires a unique value only for confirmed subscribers. Add constraint for uid whilst we are changing things.
Remaining tasks
User interface changes
1) The “Subscribers” list page (/admin/people/simplenews) changes to match the new data model.
- Rename "Active" column to "Status", with 3 values: ACTIVE, BLOCKED, UNCONFIRMED. Remove the “Unsubscribed” value from the “Subscriber Status” filter, and add to the "Status" filter.
- Unconfirmed subscriptions are now listed in their own row (instead of being part of the row for the confirmed subscriber).
2) Remove combined_body_unchanged from simplenews.settings.
API changes
- Add
Subscriber::sendConfirmation(). Subscriber::setStatus()andSubscriber::getStatus()now use an integer with 3 possible values: ACTIVE, BLOCKED, UNCONFIRMED (instead of boolean). Add shortcut functionsisActive()andisConfirmed().- Remove Subscriber::getChanges()/setChanges().
- In
Subscriber::subscribe(), deprecate the$statusparameter (it must be set to NULL or SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED). - Add new parameter
Subscriber::loadByMail($checkTrust = FALSE). If TRUE, then callskipConfirmation()and if FALSE force creating a new subscriber and set it to unconfirmed. - In
Subscriber::loadByUid(), add a new parameter$confirmed = TRUE. if TRUE only return a confirmed subscriber. - In
Subscriber::loadByMail(), add a new parameter$check_trust = FALSE. If TRUE and the current user is untrusted then force creating a new subscriber. - In SubscriptionManager::subscribe()/unsubscribe(), remove support for confirmation (the $confirm parameter must be set to FALSE). Instead the calling code needs to group changes together using a Subscriber object:
- Subscriber::loadByUid()
- Modify subscriptions
- Subscriber::sendConfirmation()
- In
SubscriptionManager, removesendConfirmations(), addConfirmation(), requiresConfirmation().
Data model changes
The Subscriber entity changes as follows:
- Remove
changesfield. - Remove the status value
SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMEDfor the subscriptions field. - Change status field to a tiny int, with 3 values: ACTIVE, BLOCKED, UNCONFIRMED.
| Comment | File | Size | Author |
|---|---|---|---|
| #8 | simplenews.confirmations-cs.3332695-8.patch | 10.71 KB | adamps |
| #5 | simplenews.confirmations.3332695-5.patch | 81.35 KB | adamps |
Comments
Comment #2
adamps commentedThanks to @ChrisZZ of "think modular" for sponsoring a detailed investigation leading to this IS update.
Comment #3
adamps commentedComment #4
adamps commentedComment #5
adamps commentedMany earlier patches for this issue were accidentally posted on #3238247: Major confusion for subscriptions during user registration. simplenews.confirmations.3332695-5.patch is identical to simplenews.registration.3238247-47.patch.
Comment #6
adamps commentedComment #7
adamps commentedSomehow the main commit for this patch 7f74512d7ab2e70c97f23aa6be53ad9cf39c3c38 was not linked here.
Comment #8
adamps commentedSecondary patch with some coding standards fixes. Probably some are connected to this issue, some not. Anyway it's good to have a commit here to ensure that credit is registered.
Comment #10
adamps commentedComment #11
adamps commentedNB this issue has 2 patches that were committed, and the first commit is not linked here.