Problem/motivation

The mail spool contains subscribers entities. When using a custom Recipient Handler it is common to want to send to user entities. This is particularly the case when using RecipientHandlerEntityBase, as shown in the example code RecipientHandlerSubscribersByRole.

Proposed resolution

  1. The Recipient Handler should write a temporary spool entry of the form 'data' = ['uid' => XX].
  2. To make that easier, adapt RecipientHandlerBase->addArrayToSpool() to accept $field = uid.
  3. Ensure that Subscriber::create() calls fillFromAccount() if the uid is set.

Comments

AdamPS created an issue. See original summary.

adamps’s picture

Issue summary: View changes
adamps’s picture

Title: Setting to automatically create a subscriber for every user » Option to automatically create a subscriber for every user
adamps’s picture

berdir’s picture

Not sure if we ported everything to 8.x, but there are/used to be newsletter options for hidden/automatic signup when registering, which should basically cover this? Not when creating users in a different way than the register form maybe, but not sure if it should really apply there.

adamps’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev
Issue summary: View changes

@Berdir yes hidden newsletters work in 8.x and that would be a workaround, although it is a little untidy to force existence of the newsletter and subscriptions that aren't actually used. As you say it only covers users created through the UI and it does not apply to pre-existing users.

I have updated the IS with my latest idea for this issue.

jonathanshaw’s picture

IS 1 & 3 yes. Seems like a clean and safe solution.

With IS 2 I'm not sure we've really got the right model nailed for what would be most helpful in these base classes, but I also think it doesn't matter much because it's all been opened up by moving addToSpool(), you've made rich examples, and we're only talking about saving people a few lines of code. Therefore I suggest you do whatever is quick and easy here, I don't see optimising further should be a priority. In my project I'm expecting to make only minimal use of the base classes and not use their helper methods.

adamps’s picture

@jonathanshaw thanks, yes I agree. Item 2 will be a simple change and derived classes are free to ignore it.

adamps’s picture

Title: Option to automatically create a subscriber for every user » Create a temporary subscriber for recipient that is User entity
adamps’s picture

Status: Active » Needs review
StatusFileSize
new5.42 KB

Status: Needs review » Needs work

The last submitted patch, 10: simplenews.user-recipient.3057125-10.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review
StatusFileSize
new5.49 KB
new405 bytes
adamps’s picture

Issue tags: +Plan to commit
jonathanshaw’s picture

  1. This whole temporary subscriber dance is fairly complex - is this the appropriate issue for adding at least one end to end test for this?
  2. +++ b/src/Entity/Subscriber.php
    @@ -111,16 +111,15 @@ class Subscriber extends ContentEntityBase implements SubscriberInterface {
    +      return user_load_by_mail($mail);
    

    The interface says this function returns \Drupal\user\UserInterface|null.

    But user_load_by_mail returns false if there is no user with that mail, and in this case the method previously returned null.

  3. In the IS you say
    In SpoolList after the call to Subscriber::create(unserialize($spool_data->data)) then if the uid is set call fillFromAccount()

    .
    This doesn't seem implemented?

  4. +++ b/src/Entity/Subscriber.php
    @@ -298,12 +297,9 @@ class Subscriber extends ContentEntityBase implements SubscriberInterface {
    +    // Fill from a User account with matching uid or email.
    +    if ($user = $this->getUser()) {
    +      $this->fillFromAccount($user);
    

    This change seems unrelated - just calling your attention to that in case it was unintentional. If I understand right, it only effects persistent subscriber entities not temporary ones.

    It also has the effect of preventing the creation of a persistent subscriber whose email is not the same as the email on his user account (because ::fillFromAccount() syncs these). But if we're insisting these are the same, why bother storing an user id reference on the subscriber at all?

  5. +++ b/src/Plugin/simplenews/RecipientHandler/RecipientHandlerBase.php
    @@ -126,13 +126,13 @@ abstract class RecipientHandlerBase extends PluginBase implements RecipientHandl
    -   *   Field to set: snid or data
    +   *   Field to set: snid, uid or data
    

    This comment, and maybe variable name, is possibly misleading (although I struggle to do better) as "uid" here is not a field to be set on the spool.

  6. +++ b/src/Plugin/simplenews/RecipientHandler/RecipientHandlerBase.php
    @@ -143,12 +143,17 @@ abstract class RecipientHandlerBase extends PluginBase implements RecipientHandl
    +    if ($field == 'uid') {
    +      $field = 'data';
    +      $values = array_map(function($v) { return ['uid' => $v]; }, $values);
    

    Worth a comment explaining?

adamps’s picture

1. Please bear with me just a little bit longer for the tests. I would like to get all the related issues done and working first so that I know the whole scenario works. This avoids the case where I would write tests then change them later.

2. Ah thanks I get it now! Fixed.

3. See 4.

4. This is the code that deals with 3. by calling fillFromAccount(). It seemed neater to let the Subscriber entity do it.

If I understand right, it only effects persistent subscriber entities not temporary ones

Not true - postCreate is called when the PHP object is created and does not rely on saving a persistent entity.

It also has the effect of preventing the creation of a persistent subscriber whose email is not the same as the email on his user account

That is already prevented. Every time the User is saved it overwrites the Subscriber - see simplenews_user_presave().

But if we're insisting these are the same, why bother storing an user id reference on the subscriber at all?

I guess it allows code to tell there is a related user without a database lookup. Anyway I'm not going to change it now.

5. Done.

6. I think the comment from 5. covers it.

Revised patch coming up soon. I just spotted a bug that the code doesn't handle the case where the uid lookup fails.

jonathanshaw’s picture

All makes sense, thanks Adam!

adamps’s picture

In SpoolList.php added a check !$subscriber->getMail() which covers the case that the User was deleted and plus any other random error that leads to a recipient without an email.

adamps’s picture

Issue summary: View changes

Status: Needs review » Needs work

The last submitted patch, 17: simplenews.user-recipient.3057125-17.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

adamps’s picture

Status: Needs work » Needs review

Random fail

  • AdamPS committed e550915 on 8.x-2.x
    Issue #3057125 by AdamPS, jonathanshaw: Create a temporary subscriber...
adamps’s picture

Status: Needs review » Fixed
Issue tags: -Plan to commit

Status: Fixed » Closed (fixed)

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