Make WordPress Core

Opened 2 years ago

Last modified 2 years ago

#57105 new enhancement

Enhance wp_handle_comment_submission() with a custom validation

Reported by: apermo's profile apermo Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version: 6.2
Component: Comments Keywords:
Focuses: Cc:

Description

As of now, there is no way to do a custom validation on comments, but it is pretty much mandatory in most European countries to ask for consent when storing the data.

In 4.4.0 the filter comment_form_fields was introduced, which allows to add a custom checkbox for that very consent.
My example below shows exactly this case, it comes with an html5 form validation via the required attribute, by due to the missing server side component, that is a weak validation(at best).

I propose to add a counterpart filter inside of wp_handle_comment_submission() that will allow exactly this missing server side validation.

<?php

/**
 * Allows a custom validation.
 *
 * @since TBD
 *
 * @param null|WP_Error $custom_validation Your possible custom error.
 * @param array         $commentdata       Array of comment data to be sent to wp_insert_comment().
 * @param array         $comment_data      The raw comment data from the function call.
 */
$custom_validation = apply_filters( 'comment_form_fields_validation', null, $commentdata, $comment_data );
if ( is_wp_error( $custom_validation ) ) {
  return $custom_validation;
}

$comment_data will pretty much contain the content of $_POST.

(Patch with exact position see attachment)

Example use case(simplied):

<?php
namespace CustomNamespace
// This is already working since 4.4.0
function comment_privacy_field( array $fields ): array {
  $fields['privacy'] = '<input id="privacy consent" name="privacy-consent" type="checkbox" value="yes" required><label for="comment-form-privacy-consent">Yes I consent...</label></p>';

  return $fields;
}
add_filter( 'comment_form_fields', __NAMESPACE__ . '\comment_privacy_field' );


// This is new in my proposal
function  comment_validation( $custom_validation, $commentdata, $comment_data ): ?\WP_Error {
  if ( ! isset( $comment_data['privacy-consent'] ) || empty( $comment_data['privacy-consent'] ) ) {
    return new \WP_Error( 'require_consent', 'You have to consent', 200 );
  }

  return $custom_validation;
}
add_filter( 'comment_form_fields_validation', __NAMESPACE__ . '\comment_validation', 10, 3 );

This would allow us to add any custom validation.

Note: Any comment form error will result in a wp_die() page, which would likely need an overhaul in this case too. But I will create a separate ticket for that topic.

Attachments (1)

comment_validation.patch (970 bytes) - added by apermo 2 years ago.
Patch for wp_handle_comment_submission to enrich it with a custom validation.

Download all attachments as: .zip

Change History (4)

@apermo
2 years ago

Patch for wp_handle_comment_submission to enrich it with a custom validation.

#1 @apermo
2 years ago

Note:

! isset( $comment_data['privacy-consent'] ) ||`

Can be removed from both the example above and the patch, since it is redundant.

#2 @SergeyBiryukov
2 years ago

Hi there, thanks for the ticket!

I might be missing something, but it looks like custom validation should already be possible via the pre_comment_approved filter, which receives $commentdata and allows for returning a WP_Error as of [41980] / #39730, pretty much like the patch suggested here.

#3 @apermo
2 years ago

Well you are mostly right. I can somehow achieve what I tried to achieve with my suggestion with that filter.

But.

My use-case as described above enhances the set of comment fields by a custom field. Which is missing in $commentdata.
While it is perfectly possible to access the data via the $_POST, I guess you will have to agree, that this is a little hacky.

I see two advantages of my suggestion.

  1. it fires earlier, I did not expect a possible exit hidden further 2 functions deep, so that would be easier to find.
  2. It contains the raw comment data, which is missing inside that filter.

I see three potential approaches to make the pre_comment_approved filter work for that use-case without the need for $_POST

  1. Add all custom form data to $commentdata.
  2. Allow to add selected custom data to $commentmeta via filter.
  3. Pass on the raw data within $comment_data all the way to pre_comment_approved, yet with a name like $raw_commentdata`

My thoughts on these, after noting them down:

  1. All data, feels wrong, and could lead to unexpected flaws, especially if there is some attack.
  2. Adding the data via filter, just to use them 2 functions deeper to get the an exit, does seem like a weird compromise, in that case I would still prefer the original proposal.
  3. That would likely be the better compromise.

I still see the advantage of my suggestion, that it is earlier and also easier to find.
Before doing the suggestion, I searched google, stackoverflow etc. and did not find anything on how to do a custom validation for the comment feature. And since I could not find anything, I went step by step into the code and into wp_handle_comment_submission and stopped there since I did not expect a filter/short circuit hidden that deep.

On top, it is arguable wether pre_comment_approved does comply with the single responsibility principle. Since it serves two purposes in a kind of way, it does set the status to 1, 0, 'spam' & 'trash' and adds a short circuit, which is at least 1.5 responsibilities.

I would argue that a distinct filter for an early rejection is beneficial, but I can agree that having two filters that serve the same purpose is even worse. So unless you see anything now that I don't see, I think the best compromise. Would be 3. (pass along the new $raw_commentdata)

If you want we can have a chat tomorrow via Slack if you need further explanation on my train of thought.

Note: See TracTickets for help on using tickets.