Skip to content

Make gdpr field required even if the setting is disabled#2282

Merged
Crabcyborg merged 6 commits into
masterfrom
make_gdpr_field_required_even_if_the_setting_is_disabled
Mar 28, 2025
Merged

Make gdpr field required even if the setting is disabled#2282
Crabcyborg merged 6 commits into
masterfrom
make_gdpr_field_required_even_if_the_setting_is_disabled

Conversation

@Crabcyborg
Copy link
Copy Markdown
Contributor

@Crabcyborg Crabcyborg commented Mar 24, 2025

Fixes the customer's issue in https://github.com/Strategy11/formidable-pro/issues/5708

It is possible for the GDPR field to not be required. I can at least replicate this by adding a field, disabling the GDPR setting, save the field, and then enabling it again. Since it is read only you end up with a checkbox that is uncheckable and unchecked.

Screen Shot 2025-03-24 at 10 04 47 AM

This update always sets the checkbox to appear as checked if the readonly_required setting is on. When this is on, we expect it to always be checked. I don't see us requiring a readonly disabled checkbox since we would likely just hide the setting in those cases.

I also added a check so it always does the required validation even when unchecked, and the required asterisk will still show even when unchecked.

Pre-release
formidable-6.19.1b.zip

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 24, 2025

Walkthrough

The changes add GDPR compliance functionality to form field validations. A new filter hook is registered in the controller to trigger GDPR-specific logic. Two methods, validate and force_required_field, have been introduced in the GDPR field model to enforce required field behavior under GDPR constraints. Additionally, the settings view has been updated to modify the checked state of a required field checkbox based on broader conditions.

Changes

File(s) Change Summary
classes/controllers/FrmHooksController.php, classes/models/fields/FrmFieldGdpr.php Added GDPR compliance logic: inserted a filter in the load_hooks method to call FrmFieldGdpr::force_required_field and introduced two new methods (validate and force_required_field) in the GDPR model to enforce required field validation.
classes/views/frm-fields/.../settings.php Updated the checkbox logic to check the field as required when either the required flag or the readonly_required flag is set.

Sequence Diagram(s)

sequenceDiagram
    participant App as Application
    participant Controller as FrmHooksController
    participant FieldGDPR as FrmFieldGdpr
    participant Validator as Form Validator

    App->>Controller: load_hooks()
    Controller->>FieldGDPR: add_filter('frm_is_field_required', force_required_field)
    Validator->>FieldGDPR: force_required_field(required, field)
    FieldGDPR-->>Validator: return updated required status
    Validator->>FieldGDPR: validate(args)
    FieldGDPR-->>Validator: return any validation errors
Loading

Possibly related PRs

  • GDPR Update #2224: Enhances GDPR compliance by modifying the FrmFieldGdpr class with a focus on required field enforcement.

Suggested labels

action: needs qa

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
classes/models/fields/FrmFieldGdpr.php (1)

162-162: Update version number in @SInCE tags.

Both new methods use "x.x" as the version number in the @SInCE tags. This should be updated to the actual version number for this release.

Replace "x.x" with the actual version number in both @SInCE tags.

Also applies to: 184-184

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 31d53f0 and 7590c69.

📒 Files selected for processing (3)
  • classes/controllers/FrmHooksController.php (1 hunks)
  • classes/models/fields/FrmFieldGdpr.php (1 hunks)
  • classes/views/frm-fields/back-end/settings.php (1 hunks)
🧰 Additional context used
🪛 GitHub Actions: Inspections
classes/models/fields/FrmFieldGdpr.php

[error] 174-174: A function call to __() with texts containing placeholders was found, but was not accompanied by a 'translators:' comment on the line above to clarify the meaning of the placeholders. (WordPress.WP.I18n.MissingTranslatorsComment)


[error] 186-186: Expected 2 spaces after parameter type; 3 found (Squiz.Commenting.FunctionComment.SpacingAfterParamType)


[error] 187-187: Expected 1 spaces after parameter type; 2 found (Squiz.Commenting.FunctionComment.SpacingAfterParamType)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Cypress
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: PHP 7.4 tests in WP trunk
  • GitHub Check: Cypress
🔇 Additional comments (2)
classes/controllers/FrmHooksController.php (1)

115-117: Appropriate hook added for GDPR field validation.

The filter hook for GDPR field validation is properly implemented. This addition ensures that GDPR fields can be forced to be required even when the required setting is disabled.

classes/views/frm-fields/back-end/settings.php (1)

88-88: Improvement to required checkbox behavior for GDPR fields.

The checkbox will now be checked when either the field is required OR when it's a readonly required field (like GDPR fields). Additionally, the readonly attribute prevents changing the state when it's a special field type that must be required.

Comment thread classes/models/fields/FrmFieldGdpr.php
Comment thread classes/models/fields/FrmFieldGdpr.php
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
classes/models/fields/FrmFieldGdpr.php (1)

159-180: Ensure the validate method handles localization properly.

The validate method correctly enforces GDPR fields as required regardless of the field settings, which aligns with the PR objectives. However, the error message generation on line 175 uses str_replace with [field_name] placeholder, which might cause issues for translators.

Consider adding a translator comment to clarify the context or switching to a sprintf approach with __() for better localization support:

-				$frm_settings                    = FrmAppHelper::get_settings();
-				$errors[ 'field' . $args['id'] ] = str_replace( '[field_name]', is_object( $this->field ) ? $this->field->name : $this->field['name'], $frm_settings->blank_msg );
+				$frm_settings = FrmAppHelper::get_settings();
+				$field_name = is_object( $this->field ) ? $this->field->name : $this->field['name'];
+				/* translators: %s: Field name */
+				$errors[ 'field' . $args['id'] ] = sprintf( __( '%s cannot be blank.', 'formidable' ), $field_name );

Alternatively, if you prefer to keep using the settings message:

+				// The blank_msg from settings already contains a [field_name] placeholder for translators
				$frm_settings                    = FrmAppHelper::get_settings();
				$errors[ 'field' . $args['id'] ] = str_replace( '[field_name]', is_object( $this->field ) ? $this->field->name : $this->field['name'], $frm_settings->blank_msg );
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7590c69 and d1bc62a.

📒 Files selected for processing (1)
  • classes/models/fields/FrmFieldGdpr.php (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
classes/models/fields/FrmFieldGdpr.php (4)
classes/models/fields/FrmFieldType.php (1)
  • validate (1369-1371)
classes/helpers/FrmFieldGdprHelper.php (2)
  • FrmFieldGdprHelper (16-73)
  • hide_gdpr_field (40-43)
classes/models/FrmField.php (1)
  • FrmField (6-1501)
classes/helpers/FrmAppHelper.php (2)
  • FrmAppHelper (6-4460)
  • get_settings (172-182)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: Cypress
🔇 Additional comments (2)
classes/models/fields/FrmFieldGdpr.php (2)

182-201: LGTM: force_required_field implementation is correct and follows best practices.

The method correctly ensures that GDPR fields are always required, even if the required setting is disabled. This implementation addresses the core issue described in the PR objectives, where GDPR checkboxes could appear uncheckable and unchecked when the setting is disabled.


159-201:

Details

✅ Verification successful

Verify integration with the hook controller.

The new methods look good, but we should verify that the force_required_field method is properly integrated with the hook system.


🏁 Script executed:

#!/bin/bash
# Description: Verify that the force_required_field method is hooked in a controller

# Find where the hook for force_required_field is registered
rg -A 2 -B 2 "frm_is_field_required.*force_required_field"

Length of output: 376


Integration Verified: force_required_field is Properly Hooked

The force_required_field method is correctly registered in the hook system as confirmed by the output in classes/controllers/FrmHooksController.php:

  • The filter frm_is_field_required is being hooked with FrmFieldGdpr::force_required_field using add_filter.

No further action is needed here.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
classes/models/fields/FrmFieldGdpr.php (2)

159-180: Good implementation of validation for GDPR fields, but consider optimizing the error message handling.

The validation logic correctly ensures that GDPR fields are required even when the required setting is disabled. The method properly extends the parent validation and only applies additional checks when necessary (when there are no errors and the GDPR field is visible).

However, I suggest improving the error message handling by:

  1. Using a more descriptive variable name for clarity
  2. Adding a comment explaining the error message substitution
public function validate( $args ) {
	$errors = parent::validate( $args );

	if ( ! $errors && ! FrmFieldGdprHelper::hide_gdpr_field() ) {
		$required = FrmField::get_option( $this->field, 'required' );

		if ( ! $required && empty( $args['value'] ) ) {
			$frm_settings                    = FrmAppHelper::get_settings();
+			// Get field name from either object or array format
+			$field_name = is_object( $this->field ) ? $this->field->name : $this->field['name'];
			$errors[ 'field' . $args['id'] ] = str_replace( '[field_name]', $field_name, $frm_settings->blank_msg );
		}
	}

	return $errors;
}

182-197: Optimize the force_required_field method with an early return.

The method correctly ensures GDPR fields are always required when visible. However, you can optimize it with an early return pattern for improved readability and efficiency.

public static function force_required_field( $required, $field ) {
+	if ( $required ) {
+		return $required;
+	}

	if ( ! $required && 'gdpr' === $field['type'] && ! FrmFieldGdprHelper::hide_gdpr_field() ) {
		$required = true;
	}

	return $required;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d1bc62a and ad8c18d.

📒 Files selected for processing (1)
  • classes/models/fields/FrmFieldGdpr.php (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
classes/models/fields/FrmFieldGdpr.php (4)
classes/models/fields/FrmFieldType.php (1)
  • validate (1369-1371)
classes/helpers/FrmFieldGdprHelper.php (2)
  • FrmFieldGdprHelper (16-73)
  • hide_gdpr_field (40-43)
classes/models/FrmField.php (1)
  • FrmField (6-1501)
classes/helpers/FrmAppHelper.php (2)
  • FrmAppHelper (6-4460)
  • get_settings (172-182)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: PHP 8 tests in WP trunk
  • GitHub Check: Cypress
  • GitHub Check: PHP 7.4 tests in WP trunk
🔇 Additional comments (1)
classes/models/fields/FrmFieldGdpr.php (1)

159-197:

Details

✅ Verification successful

Verify the integration of both methods in the form submission flow.

Both methods work well together to enforce required status for GDPR fields. The validate method handles validation during form submission, while force_required_field ensures the field is visually marked as required.

However, I don't see where force_required_field is being registered as a filter in this file or elsewhere. Make sure it's properly hooked to frm_is_field_required filter to be effective.


🏁 Script executed:

#!/bin/bash
# Check if force_required_field is properly hooked to frm_is_field_required filter
rg -A 3 -B 3 "frm_is_field_required.*force_required_field" --type php

Length of output: 522


Integration Verified: GDPR Field Methods

Both methods are properly integrated in the form submission flow. The shell script output confirms that force_required_field is correctly hooked to the frm_is_field_required filter in classes/controllers/FrmHooksController.php with the following snippet:

  • add_filter( 'frm_is_field_required', 'FrmFieldGdpr::force_required_field', 10, 2 );

No further action is needed.

Copy link
Copy Markdown
Contributor

@Liviu-p Liviu-p left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Crabcyborg, it looks good to me. Thank you for fixing it!

@Crabcyborg
Copy link
Copy Markdown
Contributor Author

Thank you Liviu!

@Crabcyborg Crabcyborg merged commit f1cad6c into master Mar 28, 2025
@Crabcyborg Crabcyborg deleted the make_gdpr_field_required_even_if_the_setting_is_disabled branch March 28, 2025 12:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants