Problem/Motivation

While prototyping experimental mailer module integration for core mails, it was found that some mails require more changes at the call site than others. For contact it is enough to swap out the contact.mail_handler. The user module requires more and riskier changes directly in the _user_mail_notify() function.

From #3534136-30: Requirements and strategy for Core Mailer by @berdir:

Contact was/is interesting in the MR because it's a separate service. Instead of replacing that conditionally in contact, we can do that in the module. It might be beneficial to push _user_mail_notify() into a service to do the same thing there as well. Then mailer could provide alternative services for all core modules that mails. And once it's no longer experimental/part of core, we move it back.

For more context see: #3536579: Use EmailTemplate config entities in core modules

Swapping out the implementation in the contact module is as easy as adding a service provider:

https://git.drupalcode.org/project/drupal/-/merge_requests/12763/diffs#7...

In comparison swapping out the implementation in the user module is messy due to _user_mail_notify():

https://git.drupalcode.org/project/drupal/-/merge_requests/12763/diffs#b...

Steps to reproduce

Proposed resolution

Extract _user_mail_notify() into an internal NotificationHandler service analogous to how this is done in contact module. The experimental mailer module then can swap out the mail handler on demand.

The new service is marked @internal and intentionally has no interface. It is discouraged to use the service outside of core. The only purpose is to simplify transition to a new mail system.

The function _user_mail_notify() is widely in use among contrib modules despite its underscore prefix. In order to reduce disruption, the function gets a longer deprecation period and is removed in Drupal 13.0.0.

Remaining tasks

Per #59:

We'd like to present two options to core committers:

Internal service, no public API (yet). This is what is currently implemented in the MR and described in the CR. The idea is to do some more investigation based on contrib usage and then decide how certain functionality should be exposed.
Public from the start. Basically drop @internal annotation from NotificationHandler and provide an old-new table in the CR (much like it was in the early days of this issue.

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Issue fork drupal-3539178

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

znerol created an issue. See original summary.

berdir’s picture

_user_mail_notify() is technically an underscore prefixed function and not covered by BC. But there are dozens of calls to it, so we should deprecate it properly.

A bit unsure if we should do a straight conversion or actually rethink things. There are various issues open about improving docs, arguments and changing how it works, such as #2582951: Use an enum for user email notification types, as a mailHandler, we should also split it into dedicated methods and make the $op stuff at least an internal thing. Deprecation should be easier than changing params on the existing function. But it will make this more complicated.

adamps’s picture

Definitely +1 for the idea in general - I agree we should end up with a user mail handler service.

I'm not sure if we need this issue specifically for user module however. As @berdir pointed out in the roadmap, every module will need a migration from old mail handler to new. Likely during the transition, the new mail handler will call the old one whenever the site uses the old mail system; the old mail handler is marked deprecated then later removed at the same time as the old mail system. (Contrib could instead use a major version change, the old branch for the old mailer and the new branch for new mailer.)

The new mail handler service could often want a different interface from the old, so we might be better to create a new service name rather than reusing the old one. We might also wish to specifically identify these new services, perhaps with a tag (which would allow checking which modules support the new mailer - perhaps a neater alternative to @berdir idea of #[SupportsMailer] on hook_mail()). Perhaps they share a base interface. Perhaps they share a way of yielding metadata. In DSM+ we have UserMailer as both a service and a plugin, and I feel it works quite well - it's an option we can consider.

Although the old mail handler for most modules is a service, the old mail handler for user is _user_mail_notify(). However it doesn't necessarily make much difference, and we can use the same migration mechanism.

In other words, we don't necessarily need a migration/deprecation step separately in this issue when we are already going to have one anyway for introducing the new mail system. I feel that it might be worth waiting until we have a clearer idea of the overall mailer design, especially the migration part, before we work on this issue.

adamps’s picture

Sorry cross-post removed a related issue - now fixed

adamps’s picture

I feel that separate functions would likely just be a nuisance in this case as the implementations are all identical (except 'register_pending_approval' generates the extra mail, but that can be a simple if test like now). The values of $op aren't really internal because they form part of the email "ID" - module/key in old terms, tag or whatever we might call it. This will be referenced in the config/code of any module that is involved in building or altering user mails. The list of $op values, and their labels is needed for the form UI that controls such config.

znerol’s picture

Issue summary: View changes
znerol’s picture

Status: Active » Needs review

#3463923: Improve documentation of _user_mail_notify regrettably ended up in a bike-shed discussion on whether it is necessary to update coding standards before committing the docs improvement. The MR in #2582951: Use an enum for user email notification types tries to change every single occurrence of an $op like string regardless of the context. By implementing the proposal from #2 (individual methods on a MailHandlerInterface), it is possible to circumvent both of these pitfalls.

The MR here turns the $op argument into an internal implementation detail. It isn't API anymore and therefore may remain a string.

#3463923-11: Improve documentation of _user_mail_notify points out an interesting angle:

I must say I don't particularly like this, the enum would be much nicer. Regardless, I think it's possible (even if ill-advised) to alter or extend the available options, so this might be needlessly restrictive.

In Drupal 7 it was indeed very easy to extend the list of possible operations via a contrib or custom code. Simply by adding a pair of variable_set() calls in an .install file ensuring that user_mail_OP_subject and user_mail_OP_body contain some meaningful text. I haven't tried it, but it still might be possible to do something like this with a smart combination of form alters and config schema hacks.

This practice should therefore be deprecated properly.

znerol’s picture

Interesting observations in #3336437: _user_mail_notify return error when email sending is aborted regarding the _user_mail_notify() return value.

znerol’s picture

Issue #85494: Use email verification when changing user email addresses seeks to add two new operations. It would be good to land this and the other issue in the same release.

andypost’s picture

CR is good but it needs details which $op is transformed into which method in new interface

Custom $op still can be implemented by decoration for the service

Othen then question about need for sendDeprecated() in public service looks RTBC

berdir’s picture

Status: Needs review » Needs work

Added some quick thoughts.

kristiaanvandeneynde’s picture

My main concern was that someone who used to use custom OPs would now have that option taken away from them. But giving it some more thought, nothing stops them from calling the mail service with said custom OP themself. So I left a few nits left and right, but nothing too major.

The new service copies the old code in ways that seem to make sense, but also do not go any further. One could argue now would be a good time to revisit some of the comments, but if you want to limit scope to "just move the code" then I could get onboard with that too.

Moshe seemed okay with the approach taken here too (via Slack) and so am I. So as far as subsystem maintainers are concerned, you are 2 for 2. Thanks for working on this.

berdir’s picture

> My main concern was that someone who used to use custom OPs would now have that option taken away from them. But giving it some more thought, nothing stops them from calling the mail service with said custom OP themself.

I'm unsure where the notion of of custom $ops comes from exactly, do we have any data that supports that this is a thing people did?

This is technically an internal function that you weren't even supposed to use, it's just a very common thing to do and convenient to use, and the two other issues that this references would have updated the docs to specify the allowed values and the other introduced an enum. both specifically disallowing the notion of a "custom $op".

Same with the underlying config. you can not add more things because you can't provide schema for it.

> The new service copies the old code in ways that seem to make sense, but also do not go any further. One could argue now would be a good time to revisit some of the comments, but if you want to limit scope to "just move the code" then I could get onboard with that too.

The idea is that we isolate the code in a way that allows us to provide an alternative implementation using the new mail API, first in an experimental module, then later moved back to user (at least that's my recommendation).

znerol’s picture

Status: Needs work » Needs review

I removed the BC for custom $op parameters. Instead _user_mail_notify now maps the $op to the correct method.

One thing to note is that this also changes the return value. Before, FALSE was returned on error and NULL when mail sending was suppressed (either by config or by a hook). The new methods return FALSE in both cases.

adamps’s picture

I'm unsure where the notion of of custom $ops comes from exactly, do we have any data that supports that this is a thing people did?

The user_registrationpassword module "sort of" makes up some new $op values. However from a quick inspection it has separate config, _user_registrationpassword_mail_notify() and _user_registrationpassword_mail(). So it likely isn't actually an example of this "thing", but it gives a hint why people might try it.

The MR here turns the $op argument into an internal implementation detail. It isn't API anymore and therefore may remain a string.

Please see #6. $op is definitely an API as any code that alters emails depends on the values. Also configuration depends on the values and the code in AccountSettingsForm must match. True, it's already a string and I agree it isn't necessarily correct to fix that by a widespread replacement everywhere in the code. Still, I feel that creation of separate API functions that make the $op appear more internal isn't necessarily helpful. It makes the interface complex and creates dull boilerplate copy/paste implementations. Could we instead create an enum on the interface??

I removed the BC for custom $op parameters.

The comment for _user_mail_notify() clearly states the allowed values for $op, so I agree we don't need to support any others.

One thing to note is that this also changes the return value.

Of course as @berdir says, _user_mail_notify is technically an internal function. We have decided that sites treat it as an API (what else can they do?) so we will do the same. For an API, a return code change is a BC break, so I feel let's leave it unchanged. We can still have our preferred return code on the new interface. We could make sendToUser() a public @internal function for BC only, which simplifies _user_mail_notify(), removing the switch statement.

adamps’s picture

In #4 I suggested to leave the code path for the old mailer almost entirely untouched, and create a new service only for the new mailer. If we make a new service in this issue, then it's not experimental. It might not be what we want for the new mailer. Because of this uncertainty I suggested postponing this issue for a few weeks at least.

kristiaanvandeneynde’s picture

I'm unsure where the notion of of custom $ops comes from exactly, do we have any data that supports that this is a thing people did?

No, and people shouldn't have. I'm trying to predict possible fallout from the changes. Like how we sometimes go the extra mile even though our BC policy says we are free to just change constructor arguments on some services: It's being nice. But I doubt we need to do that here.

The idea is that we isolate the code in a way that allows us to provide an alternative implementation using the new mail API, first in an experimental module, then later moved back to user (at least that's my recommendation).

Yeah, I got that and I like the idea. My suggestion was more that, when copying the old code into the (perhaps temporary) service, we might want to see if the comments are still up to standards. But if we want to keep the scope narrow, I'd be fine with that too. As you indicated: This code might not live very long.

adamps’s picture

We have an amazing opportunity to rewrite the entire Drupal mail ecosystem without constraints from the past. I can see so many possibilities😃. We could automatically generate the entire Email Configuration UI from simple metadata on the mailer service/plugin classes.
We could make the email building code extremely simple and clear by including a few common patterns into the Core framework (and avoid bugs where people forget those things).

We can make the migration process simple for both the site owner and developer with a carefully designed migration framework. Given that we don't have much idea yet what the migration process will be, how do we know that this issue is going to be helpful (please see #4)?

DSM+ is an example of many such things. Sure maybe we won't to do any of it in Core, but it would be great if we could at least take time to have a look at them and decide. This issue ends up constraining things that we might later want to change. We are in danger of eliminating possibilities without most people even knowing what those possibilities are.

Below is the user email code from DSM+. It replaces _user_mail_notify(), _user_mail() and most of AccountSettingsForm. See all the code that's just gone away:

  • language is automatically switched based on the 'to' address (which accepts AccountInterface)
  • token replacement is automatic from the token_types metadata (we only have to set the special token options)
  • subject/body config is stored so it can be automatically applied (the config entity id specifies the applicable mail tag, the fields specify the different parts of the email)
  • same for the 'to' address config for the admin message, defaulting to the site address
  • the config UI can be automatically built (including appropriate token help) based on the required_config metadata, we just need a single line in AccountSettingsForm to trigger its placement

All we have left is this below, the simple essentials. Of course it doesn't need to be exactly like that - there are many possible variations and improvements that follow the same ideas.


declare(strict_types=1);

namespace Drupal\symfony_mailer\Plugin\Mailer;

use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\symfony_mailer\Attribute\Mailer;
use Drupal\symfony_mailer\EmailInterface;
use Drupal\symfony_mailer\Processor\MailerPluginBase;
use Drupal\user\UserInterface;

/**
 * Defines the Mailer plug-in for user module.
 *
 * Replaces _user_mail_notify().
 */
#[Mailer(
  id: "user",
  sub_defs: [
    "cancel_confirm" => new TranslatableMarkup("Account cancellation confirmation"),
    "password_reset" => new TranslatableMarkup("Password recovery"),
    "register_admin_created" => new TranslatableMarkup("Account created by administrator"),
    "register_no_approval_required" => new TranslatableMarkup("Registration confirmation (No approval required)"),
    "register_pending_approval" => new TranslatableMarkup("Registration confirmation (Pending approval)"),
    "register_pending_approval_admin" => new TranslatableMarkup("Admin (user awaiting approval)"),
    "status_activated" => new TranslatableMarkup("Account activation"),
    "status_blocked" => new TranslatableMarkup("Account blocked"),
    "status_canceled" => new TranslatableMarkup("Account cancelled"),
  ],
  required_config: ["email_subject", "email_body"],
  token_types: ["user"],
)]
class UserMailer extends MailerPluginBase implements UserMailerInterface {

  /**
   * {@inheritdoc}
   */
  public function notify(string $op, UserInterface $user): bool {
    if ($op == 'register_pending_approval') {
      $this->newEmail("{$op}_admin")->setEntityParam($user)->send();
    }

    return $this->newEmail($op)
      ->setEntityParam($user)
      ->setTo($user)
      ->send();
  }

  /**
   * {@inheritdoc}
   */
  public function init(EmailInterface $email): void {
    $email->setParam('token_options', ['callback' => 'user_mail_tokens', 'clear' => TRUE]);
  }

}
znerol’s picture

Title: Extract _user_mail_notify() into a user MailHandler » Extract _user_mail_notify() into a user NotificationHandler
Issue summary: View changes
znerol’s picture

I restored the BC for _user_email_notify() and also the return type behavior. Dispatching op to method call in _user_mail_notify() is actually more work than keeping BC.

I also found a way to remove the interface. It is possible to mark methods for setter injection with the #[Required]. That way, a subclass doesn't need receive and pass on a MailManager via its constructor. It only needs to declare the services which it really needs.

I tried to update the issue summary in order to be more precise about the intention here. Regarding #20, this issue is about a different abstraction layer. It has no influence on the design of a future mail building layer.

Regarding the discussion whether this should be enums or separate methods: #85494: Use email verification when changing user email addresses would definitely profit from separate methods. The new email address could be passed in via an additional argument. The current MR has to tunnel it via the param array. This could be solved much better with a method with dedicated signature: https://git.drupalcode.org/project/drupal/-/merge_requests/5773/diffs#di...

znerol’s picture

Updated the deprecation message and the change record. _user_mail_notify() is replaced by an internal service. It follows, that there is no replacement API, this must be reflected in the CR.

adamps’s picture

Thanks for the clarified IS. I now understand much better what we are doing here, sorry for the prior confusion. I have made two clarifying statements below. Provided that you agree with these, then +1 from me.

The new service is marked @internal and intentionally has no interface. It is discouraged to use the service outside of core. The only purpose is to simplify transition to a new mail system.

In the new mail system, Contrib would like to be able to trigger generation of emails. I believe Easy Email might include a UI for this feature generally. Commerce has it specifically as "resend receipt".

_user_mail_notify has responsibilities which shouldn't be substituted. E.g., it has code which checks whether a notification is enabled and it logs an error if an account has no email configured. A contrib/custom module would need to replicate and maintain these responsibilities over time. This isn't desirable.

In the new mail system, Contrib would like to be able to substitute any part of the email code, and we have requirements relating to this. I wrote in more detail about this as a comment on the MR.

znerol’s picture

In the new mail system, Contrib would like to be able to trigger generation of emails. I believe Easy Email might include a UI for this feature generally. Commerce has it specifically as "resend receipt".

Yes, of course.

berdir’s picture

In the new mail system, Contrib would like to be able to trigger generation of emails. I believe Easy Email might include a UI for this feature generally. Commerce has it specifically as "resend receipt".

This API has nothing to do with the new mail system, it's explicitly an abstraction from the actual mail API, so that we can switch it out underneath.

It's not really part of the work on the new mail system at all, it fits more into the work to deprecate and move all functions in .module files to services/classes. It's just a welcome side effect that this allows to switch out the code with something that uses the upcoming new mail API, within the experimental mailer module. Because having experimental code in non-experimental modules is tricky. And once that API is no longer experimental, it will move back into user module and replace the current default implementation.

And eventually, maybe, we can then decide to deprecate this API again, if we think that working directly with the new mail API is sufficient and covers all aspects, such as enabling/disabling certain mails. Or we maybe we won't, because you could even use this abstracted service to send out SMS/app notifications instead of/additionally to e-mails. But that's several steps in the future and not relevant for now.

znerol’s picture

I went through a gitlab code search (#3051266: Searching for code across all of Gitlab) in order find out how _user_mail_notify is used in contrib. In order to exclude core clones and distros, I used the following query:

_user_mail_notify -path:core/modules/user -path:modules/contrib

That returns 228 results. I went through half of them and manually assessed how the function is being used. There are two bigger groups and a couple of interesting special cases.

Modules dealing with user registration

Modules dealing with password resets

General purpose / all ops

Special cases

GSIS (GOV.GR) Login

They are actually in the first group (Modules dealing with user registration) but they do just call $account->activate(); to trigger the registration email.

Own implementation

The following modules ship their own version of _user_pass_notify.

Custom op

The Forgot username module actually did implement a custom op (sight!).


I think we cannot deprecate _user_mail_notify as a whole without providing proper APIs to cover most of these use cases first. That said, I think the best way forward would be to deprecate custom ops here in a first step.

adamps’s picture

Great thanks @znerol.

In the new mail system, Contrib would like to be able to trigger generation of emails.

I realise that the same applies to the old mail system. That's why we have lots of calls to _user_mail_notify() even though it's technically internal.

We are deprecating _user_mail_notify(). What do we expect people to use instead? The only answer I can see is NotificationHandler. Even if we mark it internal then developers will call it as they have no choice. So I suggest that it should not be @internal and should have an interface. This would match the other existing mail handlers.

When the new mail system becomes stable then we would deprecate NotificationHandler. The developers will have to change their code again, but I don't see an easy alternative.

EDIT NB My comment was a cross-post with the previous one.

adamps’s picture

I think we cannot deprecate _user_mail_notify as a whole without providing proper APIs to cover most of these use cases first. That said, I think the best way forward would be to deprecate custom ops here in a first step.

I feel that we have an easy solution. In this issue we could create NotificationHandler with exactly the same interface as _user_mail_notify(). The only difference is that it is a service with an API rather than an internal function. The purpose of the change is to prepare for mail system migration. It becomes just the same as contact module and others.

Then later in the new mail system we switch to the new preferred interface of one function per op, we change the return code, and anything else that we want. This follows the basic principle that we avoid change to the old mail system except in rare cases that we really have to.

znerol’s picture

Issue summary: View changes
znerol’s picture

The individual functions are fine. They provide a good place for docs and we get rid of the $op strings at the call site.

I updated the issue summary and the change record. This is ready from my side.

adamps’s picture

Status: Needs review » Reviewed & tested by the community

It's not really part of the work on the new mail system at all, it fits more into the work to deprecate and move all functions in .module files to services/classes.

Thanks that is a helpful way to describe it.

This is ready from my side.

OK yes it makes sense. I am convinced, and I believe the others on the thread were already convinced a while ago.

berdir’s picture

I didn't have time to read the comments properly and review the update, just wanted to add one note for now.

I think we cannot deprecate _user_mail_notify as a whole without providing proper APIs to cover most of these use cases first. That said, I think the best way forward would be to deprecate custom ops here in a first step.

_user_mail_notify() is an internal, underscore-prefixed function. We don't *have* to do anything, we are just nice because a lot of contrib would break. Specifically, we are not forced to provide a 1:1 replacement of anything, we decide how our future API's look like and what we want to support. We can also deprecate an implicitly internal function with an explicitly internal service. It doesn't mean that it can't be called, it just means that we might change it.

What we did in some OOP hook conversion issues is that we deprecated some functions, _underscore prefixed and regular ones, without replacement, because the actual replacements are protected and can't be called. We could do something similar. We could deprecate _user_mail_notify() but *not* call the new handler, just trigger the deprecation message. Any call to the old function continues to work as-is but doesn't go through the new API. It would mean they wouldn't use the new mail API once replaced, but that's perfectly fine IMHO, especially as long as the whole thing is still experimental.

znerol’s picture

Any call to the old function continues to work as-is but doesn't go through the new API. It would mean they wouldn't use the new mail API once replaced, but that's perfectly fine IMHO, especially as long as the whole thing is still experimental.

Exactly. This is the responsibility of NotificationHandler::sendCompat(). A replacement service implemented by the mailer module either could override it (and throw an exception to make it explicit that _user_mail_notify() isn't supported by the experimental mailer). Or it could leave it alone and then a mail would be created and sent by the legacy mail system.

berdir’s picture

> Exactly. This is the responsibility of NotificationHandler::sendCompat()

Yes, my suggestion is to specifically not move that to the new service at all. Having it there means we'll need to remove sendCompat() in the future. That method currently doesn't explain that you're not supposed to call it, it explicitly "supports" an arbitrary $op and does _not_ trigger a deprecation message. forgot_username could call that directly and then we'll just break that later.

We want to deprecate _user_mail_notify() fully IMHO, we want to deprecate/remove every single .module file function we have left in core. They all need to go.

The current state to me is very confusing, with the inconsistent deprecation and what you're supposed do as a module doing that. I'd also clarify for modules like forgot_username what they are supposed to do (implement their own hook_mail())

As far as I'm concerned, we can fully deprecate _user_mail_notify() with @internal or without on NotificationHandler. IMHO, we don't need an interface to do so. An interface means we support alternative implementations, @internal means you're not supposed to call it. But happy to let a core maintainer decide on that.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review

Rerolled.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review

Rerolled.

smustgrave’s picture

Status: Needs review » Needs work

Seems to have phpstan issues?

dww’s picture

Seems to be CI weirdness where the phpstan (and phpcs) job can't download everything they need and time out...

PHPCS

Uploading artifacts for successful job
00:33
Uploading artifacts...
vendor/: found 12419 matching artifact files and directories 
composer.lock: found 1 matching artifact files and directories 
composer/Metapackage/: found 8 matching artifact files and directories 
test-artifacts/phpcs: found 2 matching artifact files and directories 
ERROR: Uploading artifacts as "archive" to coordinator... 413 Request Entity Too Large  correlation_id=3874120170 id=6932768 responseStatus=413 Request Entity Too Large status=413 token=64_YgNZ3a
FATAL: too large      

PHPStan

The following exception is caused by a process timeout
Check https://getcomposer.org/doc/06-config.md#process-timeout for details
In Process.php line 1205:
                                                                               
  The process "'git' 'clone' '--mirror' '--' 'https://github.com/phpstan/phps  
  tan.git' '/root/.composer/cache/vcs/https---github.com-phpstan-phpstan.git/  
  '" exceeded the timeout of 300 seconds.                                      

PHPUnit on PHP 8.3

The following exception is caused by a process timeout
Check https://getcomposer.org/doc/06-config.md#process-timeout for details
In Process.php line 1205:
                                                                               
  The process "'git' 'clone' '--mirror' '--' 'https://github.com/phpstan/phps  
  tan.git' '/root/.composer/cache/vcs/https---github.com-phpstan-phpstan.git/  
  '" exceeded the timeout of 300 seconds.                                      

....

dww’s picture

Status: Needs work » Needs review

Rebased the issue fork against latest 11.x to see if that resolves some of the CI weirdness...

dww’s picture

Rebase did the trick (and a manual re-run on a random fail FunctionalJavascript job), so we're back to an all-green pipeline.

I haven't reviewed this in detail, yet, so I'm not (yet) going to restore RTBC.

smustgrave’s picture

Status: Needs review » Needs work

can the deprecations be bumped 1, sorry!

znerol’s picture

Status: Needs work » Needs review

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

berdir’s picture

I realized that I prepared a review ages ago that I never posted, did that a now. Didn't reread it, but I think it's still up to date and relevant.

I still think we should do #36. We are under no obligation to provide an 1:1 replacement when deprecating something. What we're doing in a number of places with those more-or-less-internal functions is to deprecate the old function, leave it as-is (and do not try to call the new one at all), and provide a new thing that is free from the constraints and behavior of the old API.

This means that old code calling the old API would then not use the new mail handler (and it's replaced implementation), I'm perfectly OK with that.

In various places, we made those protected/internal. As mentioned, I think in this case, it is valuable to have an actual public API for this. It's not trivial, there are dozens of use cases in contrib that do stuff with users that want to send notifications for.

znerol’s picture

@berdir would you be okay with an @internal interface? This would be less exotic than the current approach.

kristiaanvandeneynde’s picture

Status: Needs review » Needs work

Bit late to review this, but +1 on where this is going. Just needs a few minor things cleaned up.

One discussion I can help decide is the private vs protected one. I won't sign off on the current state where we expect an internal class to be subclassed and then still make certain methods private. Either it's one (no subclassing) or the other (free subclassing), not a mix of both. I'd say subclassing an internal class is always an "all bets are off" situation, but we have yet to agree on what that looks like in the core issues regarding the final keyword.

znerol’s picture

Status: Needs work » Needs review

Addressed the feedback. Somebody could restart the failed job please?

kristiaanvandeneynde’s picture

Status: Needs review » Needs work

Thanks for the update, still some minor things.

kristiaanvandeneynde’s picture

Okay, after reading the IS and the comments again, and seeing how the linked MRs were actually abandoned, I'm not sure why we don't simplify this to the following:

  1. All module functions need to go (as per #36, which I agree with)
  2. Your MR neatly moves the module code for _user_mail_notify() to a service
  3. So let's deprecate the old function (and compat method on new service) and make the service the recommended thing to call going forward

Because the old function was internal, we can keep the service internal too. It's not because a whole lot of contrib projects decided to call an internal function that we should allow that to force our hand and introduce new API surface.

On the other hand, the fact that so many people called this function directly does warrant an investigation in whether we were lacking an API surface to begin with. So, with that said, I suggest the following:

  1. Deprecate function and compat method and put everything in new internal service
  2. Open a follow-up issue to investigate why so many people called this function and see if we need to remove the internal flag from the new service and make it officially supported API
znerol’s picture

znerol’s picture

Issue summary: View changes
znerol’s picture

Updated the MR, CR and the issue summary with the approach discussed in slack.

We'd like to present two options to core committers:

  1. Internal service, no public API (yet). This is what is currently implemented in the MR and described in the CR. The idea is to do some more investigation based on contrib usage and then decide how certain functionality should be exposed.
  2. Public from the start. Basically drop @internal annotation from NotificationHandler and provide an old-new table in the CR (much like it was in the early days of this issue.
znerol’s picture

Status: Needs work » Needs review
berdir’s picture

Status: Needs review » Needs work

I posted another review. FWIW, when reading this again and commented, I think the option with a secret replacement that we don't tell people about is weird and has no benefit and we should go with option 2. This leads to code duplication that contrib projects will have to adjust _again_ when doing the mailer replacement.

znerol’s picture

Status: Needs work » Needs review
znerol’s picture

Addressed the feedback, I think we have follow-ups for everything now.

berdir’s picture

Status: Needs review » Needs work

Likely last round of review from me, properly reviewed the tests as well now. I think this looks good to me then.

znerol’s picture

Status: Needs work » Needs review

Thanks @berdir.

dww’s picture

Apologies, somewhat superficial review. I'm really slammed today, but I noticed a few nits. Will try to make time later this week for more in-depth...

Thanks/sorry!
-Derek

claudiu.cristea made their first commit to this issue’s fork.

claudiu.cristea’s picture

  • Fixed the constructor argument additions deprecation removal to Drupal 12 as we did in all other issues
  • Fixed some wording in docs on the newly added service
  • Properly deprecated _user_mail_notify()
claudiu.cristea’s picture

znerol’s picture

berdir’s picture

Issue summary: View changes
Status: Needs review » Reviewed & tested by the community

This went through many iterations and discussions, per #68 @dww had planned to do another review that I think didn't really happen except follow-ups, but it has been extensively reviewed by me and others and has sign-off from the user maintainer as well.

See #59, the main question for core committers is whether or not we put an @internal on the new service or not. The issue summary documents the current variant with @internal. Note that based on my feedback in #61, some adjustments were made already. The CR and deprecation message now _do_ mention the replacement service.

dww’s picture

Yes, still slammed, haven't completed my thorough review. But don't hold this up on me. If y'all are happy, I'm happy. 😅

Thanks,
-Derek

znerol’s picture

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new665 bytes

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

smustgrave’s picture

Status: Needs work » Reviewed & tested by the community

This one legit had some phpstan issue, restoring status

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new1.55 KB

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review
berdir’s picture

Status: Needs review » Reviewed & tested by the community

Setting this back to RTBC, the only real change was the phpstan baseline adjustment.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review

#3530640: Disable the user.login.http route by default and move to REST deprecated the whole Drupal\user\Controller\UserAuthenticationController class. Therefore I removed all changes from that class. That issue also introduced a new RestAuthenticationController. Since that class isn't present in any release, there is no need to add a BC layer for the new notificationHandler property.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review

Core is moving fast these days.

berdir’s picture

Unsure about not changing UserAuthenticationController even though it's now deprecated.

In #3015812: Introduce new Theme extension object and properly deprecate REGIONS_VISIBLE and REGIONS_ALL, I did something similar with some deprecated helper methods, but there was pushback by core committers on that and we ended up also adjusting calls to deprecated methods in code that was deprecated, see #3015812-161: Introduce new Theme extension object and properly deprecate REGIONS_VISIBLE and REGIONS_ALL (comment #161). Personally I'd be fine with this, but it might be easier to get merged if we keep the changes to that controller. Maybe we can skip the constructor BC as in not triggering a deprecation message? I'll ping @godotislate on this.

znerol’s picture

Ok, added bc to UserAuthenticationController. I left out the deprecation messages.

berdir’s picture

Status: Needs review » Reviewed & tested by the community

Thanks. I think it's pretty straightforward and then I can set this back to RTBC.

quietone’s picture

I changed the title of the change record to the more typical, "X is deprecated". And I followed that up with some tweaks to the change record to focus on the change. But in doing so I see that the example in the change record is actually a diff from the MR. Instead a change record should have a 'before' and an 'after' section.

znerol’s picture

Thanks, updated the CR.

znerol changed the visibility of the branch 3539178-extract-usermailnotify-into to hidden.

znerol’s picture

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new665 bytes

The Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

nitinkumar_7’s picture

Assigned: Unassigned » nitinkumar_7
znerol’s picture

Assigned: nitinkumar_7 » Unassigned
znerol’s picture

Status: Needs work » Needs review
nicxvan’s picture

Status: Needs review » Reviewed & tested by the community

I think this is ready again, the bump to 11.5 is prudent. The suggestions looked right.

Re read the CR and it looks good.

needs-review-queue-bot’s picture

Status: Reviewed & tested by the community » Needs work
StatusFileSize
new91 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

znerol’s picture

Status: Needs work » Needs review
znerol’s picture

Had to resolve a small merge conflict because of a new import in UserPasswordForm introduced in #3593667: Convert user.module routes to attributes.

claudiu.cristea’s picture

Status: Needs review » Reviewed & tested by the community

Ready again

  • catch committed 3c3b0152 on main
    task: #3539178 Extract _user_mail_notify() into a user...
catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Patch (to be ported)

I think the internal service is good here, we can un-internal it later when we're comfortable, it doesn't stop people from using it at their own risk.

Committed/pushed to main, thanks!

Will need an 11.x backport MR.

nicxvan’s picture

Version: 11.x-dev » main
Status: Patch (to be ported) » Needs work

This is backported!

Most conflicts were the expected services and constructor.
I had to regenerate baseline.
Also $this->reflection = new \ReflectionClass($this->testClass); was in the core/modules/user/tests/src/Unit/UserRegistrationResourceTest.php but not in main. The test passes without it so I removed it here too.

I think the test failures are relevant though, let me run them locally to confirm.
UserJsonBasicAuthDecorate
UserLoginHttpDecorated

nicxvan’s picture

Status: Needs work » Needs review

@znerol was able to point out the controller was missing the right types:
UserAuthenticationInterface|UserAuthInterface $user_auth,

This had already been cleaned up on main and it's from a separate request so I couldn't see the direct error.

znerol’s picture

Status: Needs review » Reviewed & tested by the community

The $reflection property was removed from UserRegistrationResourceTest in #3581407: Remove unused properties from unit tests without a backport.

  • catch committed c222dedb on 11.x
    task: #3539178 Extract _user_mail_notify() into a user...
catch’s picture

Version: main » 11.x-dev
Status: Reviewed & tested by the community » Fixed

Committed/pushed to 11.x, thanks!

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

quietone’s picture

Updated the branch in the change record and published, adjusting date to the time of the commit.

Status: Fixed » Closed (fixed)

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