-
Notifications
You must be signed in to change notification settings - Fork 92
gpi-waiting-list.php: Fixed an issue where the waiting list message was not displaying correctly in entry views.
#1197
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
… was not displaying correctly in entry views.
WalkthroughA new filter handler Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (1)
gp-inventory/gpi-waiting-list.php (1)
140-155: Prefer strict comparisons + guard against missing choices.
$choice['text'] != $value/$choice['value'] != $valuecan behave oddly with type juggling; andforeach ( $field->choices as $choice )can warn if choices is null. Consider strict compares + a quickempty( $field->choices )early return.private function is_entry_item_waitlisted( $entry, $field, $value ) { if ( gp_inventory_type_choices()->is_applicable_field( $field ) ) { + if ( empty( $field->choices ) || ! is_array( $field->choices ) ) { + return false; + } foreach ( $field->choices as $choice ) { - if ( $choice['text'] != $value && $choice['value'] != $value ) { + if ( $choice['text'] !== $value && $choice['value'] !== $value ) { continue; } return (bool) gform_get_meta( $entry['id'], sprintf( 'gpi_is_waitlisted_%d_%s', $field->id, sanitize_title( $choice['value'] ) ) ); } }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
gp-inventory/gpi-waiting-list.php(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
gp-inventory/gpi-waiting-list.php (3)
gp-inventory/gpi-weighted-inventory.php (1)
is_applicable_field(102-104)gp-nested-forms/gpnf-require-unique-values.php (1)
is_applicable_field(80-85)gravity-forms/gw-simple-approval.php (1)
is_applicable_form(80-85)
| // Add support for order summary in entry details | ||
| add_filter( 'gform_product_info', array( $this, 'add_waitlist_message_to_product_info' ), 10, 3 ); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cat -n gp-inventory/gpi-waiting-list.php | head -200Repository: gravitywiz/snippet-library
Length of output: 8795
🏁 Script executed:
rg "gform_product_info" --type php -B 3 -A 3 | head -100Repository: gravitywiz/snippet-library
Length of output: 9008
🏁 Script executed:
rg "apply_filters.*gform_product_info|do_action.*gform_product_info" --type php -B 5 -A 5Repository: gravitywiz/snippet-library
Length of output: 52
🌐 Web query:
Gravity Forms gform_product_info hook documentation entry parameter
💡 Result:
The gform_product_info filter receives three parameters: (1) $product_info (array), (2) $form (Form object), and (3) $entry — the Entry object for the entry being processed (examples sometimes name it $lead). [1]
Source: Gravity Forms docs for gform_product_info. [1]
🏁 Script executed:
cat -n gp-inventory/gpi-waiting-list.php | sed -n '189,250p'Repository: gravitywiz/snippet-library
Length of output: 2439
🏁 Script executed:
rg "gform_product_info" -A 10 gravity-forms/gw-gravity-forms-filter-out-zero-dollar-products.php | head -30Repository: gravitywiz/snippet-library
Length of output: 559
🏁 Script executed:
rg "gform_product_info" -A 15 gs-product-configurator/gspc-display-discounts-on-entry.php | head -25Repository: gravitywiz/snippet-library
Length of output: 831
🌐 Web query:
Gravity Forms gform_product_info hook when called contexts entry detail order summary
💡 Result:
-
The gform_product_info filter is fired whenever Gravity Forms "requests the list of products saved in an entry" — i.e., any time GF builds the product/line‑item data for an entry. [1]
-
Typical contexts where it runs: Entry Detail order grid / order summary, the {all_fields} and {pricing_fields} merge tags (order summary output), notification/confirmation generation, and when Add‑Ons (e.g. PayPal) prepare product data to send. Use gform_order_summary / gform_product hooks to alter markup/labels. [2][3]
-
Important: gform_product_info applies when creating/assembling product data; it does NOT fire for already cached entry product data unless you clear that cached data first. [1]
Sources:
[1] Gravity Forms — gform_product_info documentation.
[2] Gravity Forms — gform_order_summary documentation.
[3] Gravity Forms — gform_product documentation.
🌐 Web query:
Gravity Forms gform_product_info preview mode draft entry null missing
💡 Result:
Short answer: This is expected — gform_product_info runs when Gravity Forms "requests the list of products saved in an entry", but Preview / Save‑and‑Continue uses an incomplete (draft) submission where the cached entry/product data can be null, so the product list may be missing in preview mode. Use the Save & Continue filters or the preview init hook to rebuild/inject the product info for draft submissions before the preview renders. [1][2]
What to do (concise):
- Confirm the problem: gform_product_info will not fire with complete product data when Gravity Forms loads a cached/draft entry. See docs. [1]
- Solution A — rebuild the draft submission product data: hook gform_incomplete_submission_post_get, decode the submission JSON, populate the partial_entry / submitted_values (or add the missing product info) and re-encode it. This ensures the preview has product data available. [2]
- Solution B — if you only need to alter product output on preview, use gform_preview_init and run code to generate the products from the form/posted values or from the incomplete submission token. [2][7] (gform_preview_init doc).
- Also check for third‑party Previewer/GravityView/Preview add‑ons (they sometimes cache or alter preview behavior) and test with other plugins disabled.
Minimal example (outline — adapt to your field IDs and form):
- add_filter('gform_incomplete_submission_post_get', 'my_fix_draft_products', 10, 3);
- in function: $obj = json_decode($submission_json); update $obj->partial_entry (or $obj->submitted_values) to include pricing/product values; return json_encode($obj);
References:
- gform_product_info (behavior and note about cached entries). [1]
- gform_incomplete_submission_post_get (how to modify draft submission after retrieval). [2]
If you want, paste your form ID and product field IDs and I’ll provide a ready‑to‑drop PHP snippet.
Add guards in add_waitlist_message_to_product_info() for incomplete entry data in preview/draft contexts.
The gform_product_info hook can fire when processing incomplete entries (e.g., Save & Continue previews, draft submissions) where the entry structure may be incomplete or missing the id key. The callback at line 169-187 immediately calls is_entry_item_waitlisted(), which accesses $entry['id'] without checking if it exists. Add a guard before processing:
public function add_waitlist_message_to_product_info( $product_info, $form, $entry ) {
if ( ! $this->is_applicable_form( $form ) ) {
return $product_info;
}
+ if ( empty( $entry['id'] ) ) {
+ return $product_info;
+ }This prevents notices/fatals when the entry is not fully persisted.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In gp-inventory/gpi-waiting-list.php around lines 52-54 and the callback at
lines ~169-187, the gform_product_info handler calls is_entry_item_waitlisted()
which assumes $entry['id'] exists; add a guard at the start of
add_waitlist_message_to_product_info() that verifies $entry is an array and
isset($entry['id']) (or a similar truthy check) and return early if not present
(this covers preview/draft/incomplete entries), so the function skips processing
when entry data is incomplete and avoids notices/fatals.
| if ( $this->is_entry_item_waitlisted( $entry, $field, $value ) && strpos( $value, $this->waitlist_message ) === false ) { | ||
| $value .= ' ' . $this->waitlist_message; | ||
| } | ||
|
|
||
| $is_waitlisted = gform_get_meta( $entry['id'], sprintf( 'gpi_is_waitlisted_%d_%s', $field->id, sanitize_title( $choice['value'] ) ) ); | ||
| return $value; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid strpos() on non-strings.
If $value is ever non-string (or null), strpos() can throw a TypeError on newer PHP versions. A lightweight cast (or is_string() check) keeps this robust.
- if ( $this->is_entry_item_waitlisted( $entry, $field, $value ) && strpos( $value, $this->waitlist_message ) === false ) {
+ $value_str = (string) $value;
+ if ( $this->is_entry_item_waitlisted( $entry, $field, $value_str ) && strpos( $value_str, $this->waitlist_message ) === false ) {
- $value .= ' ' . $this->waitlist_message;
+ $value_str .= ' ' . $this->waitlist_message;
}
- return $value;
+ return $value_str;Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In gp-inventory/gpi-waiting-list.php around lines 162 to 167, the code calls
strpos($value, $this->waitlist_message) which can throw a TypeError if $value is
not a string or is null; guard the call by either casting $value to string
before using strpos (e.g. $valStr = (string) $value) or checking
is_string($value) first, then append the waitlist message only when appropriate;
ensure the function still returns the original $value type when no change is
needed.
| public function add_waitlist_message_to_product_info( $product_info, $form, $entry ) { | ||
| if ( ! $this->is_applicable_form( $form ) ) { | ||
| return $product_info; | ||
| } | ||
|
|
||
| if ( gp_inventory_type_simple()->is_applicable_field( $field ) || gp_inventory_type_advanced()->is_applicable_field( $field ) ) { | ||
| $is_waitlisted = gform_get_meta( $entry['id'], sprintf( 'gpi_is_waitlisted_%d', $field->id ) ); | ||
| foreach ( $product_info['products'] as $field_id => &$product ) { | ||
| $field = GFAPI::get_field( $form, $field_id ); | ||
|
|
||
| if ( $is_waitlisted ) { | ||
| $value .= ' ' . $this->waitlist_message; | ||
| if ( ! $this->is_applicable_field( $field ) ) { | ||
| continue; | ||
| } | ||
|
|
||
| if ( $this->is_entry_item_waitlisted( $entry, $field, $product['name'] ) && strpos( $product['name'], $this->waitlist_message ) === false ) { | ||
| $product['name'] .= ' ' . $this->waitlist_message; | ||
| } | ||
| } | ||
|
|
||
| return $value; | ||
| return $product_info; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard $entry, $product_info['products'], and missing fields; unset foreach reference.
This callback assumes (1) $entry['id'] exists, (2) $product_info['products'] is populated, and (3) GFAPI::get_field() always returns a valid field. Missing these can cause PHP warnings or fatal errors. Additionally, the foreach reference should be unset after the loop to prevent accidental mutations if code is modified later.
public function add_waitlist_message_to_product_info( $product_info, $form, $entry ) {
if ( ! $this->is_applicable_form( $form ) ) {
return $product_info;
}
+ if ( empty( $entry ) || ! is_array( $entry ) || empty( $entry['id'] ) ) {
+ return $product_info;
+ }
+
+ if ( empty( $product_info['products'] ) || ! is_array( $product_info['products'] ) ) {
+ return $product_info;
+ }
+
foreach ( $product_info['products'] as $field_id => &$product ) {
$field = GFAPI::get_field( $form, $field_id );
+ if ( ! $field ) {
+ continue;
+ }
if ( ! $this->is_applicable_field( $field ) ) {
continue;
}
if ( $this->is_entry_item_waitlisted( $entry, $field, $product['name'] ) && strpos( $product['name'], $this->waitlist_message ) === false ) {
$product['name'] .= ' ' . $this->waitlist_message;
}
}
+ unset( $product );
return $product_info;
}Note: $product['name'] is the correct field for matching products across all product field types (single, dropdown, radio, select, checkbox).
| if ( $available <= 0 ) { | ||
| $message = $this->waitlist_message; | ||
| $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . $message . '</div>' . $field->description; | ||
| $message = $this->waitlist_message; | ||
| if ( strpos( $field->description, $this->waitlist_message ) === false ) { | ||
| $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . $message . '</div>' . $field->description; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strpos( $field->description, ... ) can TypeError if description is null.
Cast to string (or default to empty string) before strpos(). Also, if waitlist_message is meant to be plain text, escape it when injecting into HTML.
if ( $available <= 0 ) {
$message = $this->waitlist_message;
- if ( strpos( $field->description, $this->waitlist_message ) === false ) {
- $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . $message . '</div>' . $field->description;
+ $description = (string) $field->description;
+ if ( strpos( $description, $this->waitlist_message ) === false ) {
+ $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . esc_html( $message ) . '</div>' . $description;
}
$field->isWaitlisted = true;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if ( $available <= 0 ) { | |
| $message = $this->waitlist_message; | |
| $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . $message . '</div>' . $field->description; | |
| $message = $this->waitlist_message; | |
| if ( strpos( $field->description, $this->waitlist_message ) === false ) { | |
| $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . $message . '</div>' . $field->description; | |
| } | |
| if ( $available <= 0 ) { | |
| $message = $this->waitlist_message; | |
| $description = (string) $field->description; | |
| if ( strpos( $description, $this->waitlist_message ) === false ) { | |
| $field->description = '<div class="gpi-available-inventory-message" style="padding-bottom: 13px;">' . esc_html( $message ) . '</div>' . $description; | |
| } |
🤖 Prompt for AI Agents
In gp-inventory/gpi-waiting-list.php around lines 276 to 280,
strpos($field->description, $this->waitlist_message) can throw a TypeError when
description is null and the waitlist_message is injected into HTML unescaped;
ensure $field->description is treated as a string (e.g., default to '' or cast
to (string)) before calling strpos, and escape the waitlist message when
concatenating into HTML (use the appropriate escaping function such as esc_html
or htmlspecialchars) so you pass safe text into the markup.
Context
⛑️ Ticket(s): https://secure.helpscout.net/conversation/3165078090/94962?viewId=3808239
Summary
Issue: The waiting list message was not displaying consistently across entry views. Choice-based fields with "Show value" enabled were missing the message on the Entry List page because only the choice text was being compared, not the value. The Entry Details order summary never showed the message because it uses its own display logic. Additionally, single product fields displayed the message twice on the form due to the filter running multiple times.
Fix: Updated
add_waitlist_message_to_entry_value()to compare against both choice text and value. Added agform_product_infofilter to handle the order summary section. Extracted the waitlist check logic into a sharedis_entry_item_waitlisted()helper to reduce duplication between the two methods. Added a duplicate check inadd_waiting_list_to_single_product()to prevent double messages.