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:

  1. Store each unconfirmed subscription in a separate subscriber object
  2. Add a new field to indicate the unconfirmed status and remove SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED
  3. Remove Subscriber::getChanges() and Subscriber::setChanges()
  4. When the subscription is confirmed, search for any existing subscription and merge them together
  5. These changes are not back-compatible so require a new 4.x branch.
  6. Also see all the "changes" sections below.

Details:

  1. In SubscriptionManager, remove sendConfirmations(), destruct(), addConfirmation(), requiresConfirmation($uid).
  2. In ConfirmationController::confirmCombined(), delete the block relating to no changes available. Remove the changes from the hash, and same in simplenews_tokens(). In the ‘else’ block around line 103, call $subscriber->setStatus(SubscriberInterface::ACTIVE) and same in ConfirmMultiForm::submitForm().
  3. In SubscriptionsBlockForm ::submitExtra(), if the subscriber isn’t confirmed then call sendConfirmation(). Remove the call to subscription manager, instead save directly to the subscriber.
  4. 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() and Subscriber::getStatus() now use an integer with 3 possible values: ACTIVE, BLOCKED, UNCONFIRMED (instead of boolean). Add shortcut functions isActive() and isConfirmed().
  • Remove Subscriber::getChanges()/setChanges().
  • In Subscriber::subscribe(), deprecate the $status parameter (it must be set to NULL or SIMPLENEWS_SUBSCRIPTION_STATUS_SUBSCRIBED).
  • Add new parameter Subscriber::loadByMail($checkTrust = FALSE). If TRUE, then call skipConfirmation() 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, remove sendConfirmations(), addConfirmation(), requiresConfirmation().

Data model changes

The Subscriber entity changes as follows:

  • Remove changes field.
  • Remove the status value SIMPLENEWS_SUBSCRIPTION_STATUS_UNCONFIRMED for the subscriptions field.
  • Change status field to a tiny int, with 3 values: ACTIVE, BLOCKED, UNCONFIRMED.

Comments

AdamPS created an issue. See original summary.

adamps’s picture

Issue summary: View changes

Thanks to @ChrisZZ of "think modular" for sponsoring a detailed investigation leading to this IS update.

adamps’s picture

Issue summary: View changes
adamps’s picture

Issue summary: View changes
adamps’s picture

Status: Active » Needs review
StatusFileSize
new81.35 KB

Many 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.

adamps’s picture

Version: 3.x-dev » 4.x-dev
Status: Needs review » Fixed
adamps’s picture

Somehow the main commit for this patch 7f74512d7ab2e70c97f23aa6be53ad9cf39c3c38 was not linked here.

adamps’s picture

Status: Fixed » Needs review
StatusFileSize
new10.71 KB

Secondary 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.

  • AdamPS committed 9744711d on 4.x
    Issue #3332695 by AdamPS: Model for confirmations is flawed (coding...
adamps’s picture

Status: Needs review » Fixed
adamps’s picture

NB this issue has 2 patches that were committed, and the first commit is not linked here.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.