Problem/Motivation

It's possible to create a user with no email address.
Enabling contact.module, and then trying to send a message to that user fails. (With not enough explanation to the sender about what went wrong)

To replicate

* Given that contact module is enabled
* And I am logged in as admin
* When I "Add user" at /admin/people/create
* - Observe that "E-mail address" is not required.

* Given that I create a user with NO email.
* And I tick "Personal contact form"
* When I visit the user account page
* And I press "Contact"
* And I use the form to send a message
* Then I see an error message "Unable to send e-mail. Contact the site administrator if the problem persists."
* And I see an error in the logs "Error sending e-mail (from me@example.com to with reply-to me@example.com)."

Optional email addresses is explained in AccountFormController::form()

    // The mail field is NOT required if account originally had no mail set
    // and the user performing the edit has 'administer users' permission.
    // This allows users without e-mail address to be edited and deleted.

So this appears to be by design. HOWEVER, maybe it is unintentional that email is always optional for new accounts. If so maybe that is a separate issue.

Proposed resolution

Disable access to the personal contact form for users with no valid email.

User interface changes

contact.module contact form should be unavailable for such users.
It should not be possible for such user accounts to enable "Personal contact form" on their account.

Beta phase evaluation

Reference: https://www.drupal.org/core/beta-changes
Issue category Bug, as this permits a user to fill out a form that can never be successfully transmitted.
Issue priority Normal, as this does not break primary functionality but results in undesirable behaviour in only specific cases
Prioritized changes This is a prioritized issue as it will improve usability and result in a more logical UX.
Disruption No disruption expected, as the changes are limited to the "contact" module and should not interract in any significant way with other code.
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dman’s picture

Issue summary: View changes
dman’s picture

Issue summary: View changes
dman’s picture

Issue summary: View changes
akozma’s picture

Assigned: Unassigned » akozma
akozma’s picture

Status: Active » Needs review
FileSize
3.81 KB

An ajax event is added to the mail form element. The personal contact form settings are disabled/enabled depending on the mail field.
I used #ajax to change the disabled element message too in order to give a feedback for the current user.

larowlan’s picture

I don't think ajax is the answer here. I think a 404 for said users at user/{uid}/contact make more sense

larowlan’s picture

Ie we need both

larowlan’s picture

FileSize
1.09 KB

something like this

larowlan’s picture

FileSize
2.42 KB

and some tests

dman’s picture

Oo tests! Yay.

The test seems weak however. If I run the test-only, *without* the code patch, the test still runs green. Which indicates the test proves nothing new.
Why would that be? The code seems sound enough... I need to check manually.

In Manual testing I'm still seeing the tab on the user page (and still able to use the contact form, as admin at least).
That tab should just not be there.
... Admin seems to get a free pass as usual, but in this case, even admin probably should not.
// User administrators should always have access to personal contact forms.
Maybe the no-email logic needs to be above this.

Manual testing, as non admin, an authenticated user with 'View user profiles' and 'Use users' personal contact forms' permissions on ...
I do see the contact tab it for an account with a valid mail, normal.
But I also see it for an account with no mail. I am able to see the tab, get to the page, and able to trigger the error still.

Maybe it's just me, I need to reset my test system from scratch again maybe. Something seems wrong.

larowlan’s picture

Na you're right, need to go before the admin bit. Also, tab access might be different, will have to take a look.

andypost’s picture

tabs (local tasks) are differerent plugins, so probably re-use menu-links access checking

jhedstrom’s picture

Category: Task » Bug report
Status: Needs review » Needs work
Issue tags: +Needs reroll

Patch no longer applies. I'd say an error that can be produced through the UI qualifies as a bug rather than a task.

tibbsa’s picture

I tried to work on a bit of a reroll of this, but a few things came up that persuaded me to stop and see if there was a better way to proceed. This will give a couple of hints if someone else wants to take a stab at this.

1. If the ajax callback is to be kept, note that valid_email_address() is on its way to the graveyard and we should be using the email validation service instead:

return \Drupal::service('email.validator')->isValid($mail);

See: #2343043: valid_email_address() should use egulias/EmailValidator and become deprecated

2. The return values for access() have changed. The following works, which I would insert between the 'self-contact' test and the administrator override:

   // It is not possible to contact users with no email address defined.
    if (!$contact_account->getEmail()) {
      return AccessResult::forbidden();
    }

Now to the part that my relatively novice knowledge of the routing system, etc. has not prepared me for.

I tend to agree that, if we are going to throw an error on visiting user/{uid}/contact for someone without an email address defined, it seems more logical to return 404 (not found) rather than 403 (access denied). The latter is a more accurate description of the error.

Ergo, it seems to me a little bit of a kludge to implement this at the 'access' level. ContactController::contactSitePage() throws a NotFoundHttpException() if a user tries to visit a non-existent default site form, and we can do the same in ContactController::contactPersonalPage(). That works as expected.

However, that doesn't hide the Contact tab on the user profile. This was done in prior patches by denying access to the tab. But is there a better way to disable this without hacking a check into the access control layer?

The problem with doing it in the access check is that this fires before the controller callback, resulting in a 403 Forbidden error being returned to the client when visiting user/{uid}/contact, rather than a "not found" error (since ContactController::contactPersonalPage() never executes to throw the more meaningful 404 error). Throwing that error in the access() callback is not an option because that presumably breaks the user profile page, too.

dman’s picture

Good analysis tibbsa . Thanks for thinking about the message it sends.

tibbsa’s picture

(Working on this...)

tibbsa’s picture

Assigned: akozma » tibbsa
Priority: Minor » Normal
Issue summary: View changes

Added beta evaluation.

tibbsa’s picture

Assigned: tibbsa » Unassigned
Status: Needs work » Needs review
Issue tags: -Needs reroll
FileSize
2.31 KB
2.24 KB

Two patches are attached.

The first patch fixes the problem described in the issue description:

  1. the "Contact" tab is hidden on user profiles where the user has no email address (for all users);
  2. if someone indirectly finds their way to user/{nid}/contact for a user with no e-mail address, a 404 error is returned

This does not yet deal with the account profile form or include the Ajax code provided above, as this has to be completely re-factored by the looks of it, and quite simply, I've not figured out even how to add additional validation to the form never mind Ajax validation.

The tests check for both of these conditions, and I expect two failures to be reported.

Before moving on to 'enhancements' with respect to the account edit forms, are we satisfied that this at least prevents the identified bug from manifesting?

Status: Needs review » Needs work

The last submitted patch, 18: contact-form-2225597-18-tests.patch, failed testing.

tibbsa’s picture

Status: Needs work » Needs review
jhedstrom’s picture

@tibbsa, can you roll a patch that includes the tests and the fix in a single file, to illustrate that the fix truly addresses the failing tests?

tibbsa’s picture

FileSize
4.56 KB

Combined patch + tests.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

This does not yet deal with the account profile form or include the Ajax code provided above, as this has to be completely re-factored by the looks of it, and quite simply, I've not figured out even how to add additional validation to the form never mind Ajax validation.

I think it is OK that this doesn't take the ajax approach discussed above. Simply removing the tab and setting a 404 for the page makes sense to me.

tstoeckler’s picture

Can maybe be fixed pre-commit so not setting to "needs work":

  1. +++ b/core/modules/contact/contact.module
    @@ -85,6 +86,27 @@ function contact_entity_extra_field_info() {
    + * Hide the 'Contact' tab on the user profile if the user does not have
    + * an email address configured.
    

    I think it would be more consistent to say "Hides", instead of "Hide". Also, this breaks before 80 chars.

  2. +++ b/core/modules/contact/contact.module
    @@ -85,6 +86,27 @@ function contact_entity_extra_field_info() {
    +function contact_menu_local_tasks_alter(&$data, $route_name)
    +{
    

    The opening brace should be on the same line as the function definition

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 22: contact-form-2225597-22.patch, failed testing.

tibbsa’s picture

Status: Needs work » Needs review
FileSize
4.56 KB

Fixed the minor issues identified in #24, and re-rolled.

jhedstrom’s picture

Status: Needs review » Reviewed & tested by the community

I think this is back to RTBC.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed f067cd5 and pushed to 8.0.x. Thanks!

Thank you for adding the beta evaluation to the issue summary.

  • alexpott committed f067cd5 on 8.0.x
    Issue #2225597 by tibbsa, larowlan, akozma: contact.module allows you to...

Status: Fixed » Closed (fixed)

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