-
Notifications
You must be signed in to change notification settings - Fork 92
gpnf-duplicate-child-entry-multiple-times.js
: Added new snippet.
#1168
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
Conversation
WalkthroughAdds a new JavaScript snippet for Gravity Perks Nested Forms that enables duplicating a child entry multiple times, with prompt-driven copy count, per-target configuration, sequential AJAX duplication, UI refreshes, and a public instance method to trigger the flow. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant Trigger as UI Trigger
participant GPNF as GPNF Instance
participant Server as AJAX Endpoint (gpnf_duplicate_entry)
participant UI as GPNestedForms UI
User->>Trigger: Click "Duplicate"
Trigger->>GPNF: gpnf.duplicateEntry(entryId, $trigger)
GPNF->>User: Prompt for copy count
alt Valid count within limits
loop For each copy (sequential)
GPNF->>Server: POST duplicate_once(entryId, context)
Server-->>GPNF: Success or Error
alt Success
GPNF->>UI: GPNestedForms.loadEntry(...)
GPNF->>GPNF: gform doAction (notify)
else Error
GPNF-->>User: Alert error and stop sequence
end
end
GPNF-->>User: Re-enable trigger, end
else Invalid/zero count
GPNF-->>User: Cancel / no-op
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests
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: 1
🧹 Nitpick comments (6)
gp-nested-forms/duplicate-child-entry-multiple-times.js (6)
74-91
: Input handling: trim, sanitize, and safer current-count lookup.Trim the prompt input, keep integer parsing explicit, and tolerate either observable or array for
viewModel.entries
.gpnf.duplicateEntry = function( entryId, $trigger ) { var message = config.prompt || 'How many times should this entry be duplicated?'; - var input = window.prompt( message, '1' ); + var input = ( window.prompt( message, '1' ) || '' ).trim(); if ( input === null ) { return; } - var copies = parseInt( input, 10 ); + var copies = parseInt( input, 10 ); if ( isNaN( copies ) || copies < 1 ) { copies = 1; } @@ if ( max !== '' && max != null ) { max = parseInt( max, 10 ); if ( ! isNaN( max ) ) { - var current = gpnf.viewModel && gpnf.viewModel.entries ? gpnf.viewModel.entries().length : 0; + var current = 0; + if ( gpnf.viewModel && gpnf.viewModel.entries ) { + if ( typeof gpnf.viewModel.entries === 'function' ) { + current = gpnf.viewModel.entries().length; + } else if ( Array.isArray( gpnf.viewModel.entries ) ) { + current = gpnf.viewModel.entries.length; + } + } copies = Math.min( copies, Math.max( max - current, 0 ) ); } }Also applies to: 99-106
112-117
: Accessibility: reflect disabled state for non-button triggers.Anchors won’t honor
disabled
; addaria-disabled
/aria-busy
and an optional CSS class.var disableTarget = $trigger && typeof $trigger.prop === 'function' ? $trigger : null; if ( disableTarget ) { - disableTarget.prop( 'disabled', true ); + disableTarget.prop( 'disabled', true ) + .attr( { 'aria-disabled': 'true', 'aria-busy': 'true' } ) + .addClass( 'is-disabled' ); } @@ } ).always( function() { if ( disableTarget ) { - disableTarget.prop( 'disabled', false ); + disableTarget.prop( 'disabled', false ) + .attr( { 'aria-disabled': 'false' } ) + .removeAttr( 'aria-busy' ) + .removeClass( 'is-disabled' ); } } );
118-124
: Behavioral choice: stop-on-first-failure vs continue.Current queue aborts on first failure. If partial success is preferable, catch per-iteration and continue, then surface a summary error.
Do you want the queue to continue duplicating after a single failure? If yes, I can provide a revised loop with per-call error capture.
137-151
: Form-context check could be stricter.If multiple forms with the same Nested Field ID are present and
GFFORMID
is undefined, this may initialize across forms. Prefer checkinggpnf.$form
against the current form element if available.function matchesForm( gpnf ) { if ( typeof gpnf.formId !== 'number' ) { return false; } if ( typeof GFFORMID !== 'undefined' ) { var currentFormId = parseInt( GFFORMID, 10 ); if ( ! isNaN( currentFormId ) ) { return gpnf.formId === currentFormId; } } - return true; + return !!( gpnf.$form && gpnf.$form.length ); }
153-161
: Avoid iterating inherited props onwindow
.Guard with
hasOwnProperty
to skip prototype pollution and non-enumerables.- for ( var key in window ) { - if ( key.indexOf( 'GPNestedForms_' ) === 0 ) { - init( window[ key ] ); - } - } + for ( var key in window ) { + if ( Object.prototype.hasOwnProperty.call( window, key ) && key.indexOf( 'GPNestedForms_' ) === 0 ) { + init( window[ key ] ); + } + }
16-25
: Config UX: optional hard cap when no entry limit is set.If
entryLimitMax
is unset, consider a conservative default cap (e.g., 50) to prevent accidental large queues.Do you want me to add a
defaultMaxCopies
fallback applied only when neithermaxCopies
nor an entry limit is present?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
gp-nested-forms/duplicate-child-entry-multiple-times.js
(1 hunks)
🔇 Additional comments (1)
gp-nested-forms/duplicate-child-entry-multiple-times.js (1)
74-134
: No method-name collision — local override only. Repo search found gpnf.duplicateEntry defined only in gp-nested-forms/duplicate-child-entry-multiple-times.js (line 74); no other definitions or call sites detected.
// Wrap GPNF's duplicate endpoint in a promise so we can queue requests sequentially. | ||
var duplicateOnce = (function() { | ||
var nonce = window.GPNFData && window.GPNFData.nonces ? window.GPNFData.nonces.duplicateEntry : ''; | ||
|
||
return function( entryId ) { | ||
return $.post( gpnf.ajaxUrl, { | ||
action: 'gpnf_duplicate_entry', | ||
nonce: nonce, | ||
gpnf_entry_id: entryId, | ||
gpnf_parent_form_id: gpnf.formId, | ||
gpnf_nested_form_field_id: gpnf.fieldId, | ||
gpnf_context: gpnf.ajaxContext | ||
} ).then( function( response ) { | ||
if ( ! response || ! response.success ) { | ||
return $.Deferred().reject( response && response.data ? response.data : 'Unable to duplicate entry.' ); | ||
} | ||
|
||
if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) { | ||
window.GPNestedForms.loadEntry( response.data ); | ||
} | ||
|
||
if ( window.gform && typeof window.gform.doAction === 'function' ) { | ||
window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response ); | ||
} | ||
|
||
return response; | ||
} ); | ||
}; | ||
})(); |
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.
Fix: call instance loadEntry
; add nonce guard; harden AJAX.
Use the instance method (gpnf.loadEntry
) rather than a global static, fail fast if the nonce is missing, and add a timeout/dataType for robustness.
// Wrap GPNF's duplicate endpoint in a promise so we can queue requests sequentially.
var duplicateOnce = (function() {
- var nonce = window.GPNFData && window.GPNFData.nonces ? window.GPNFData.nonces.duplicateEntry : '';
+ var nonce = window.GPNFData && window.GPNFData.nonces
+ ? (window.GPNFData.nonces.duplicateEntry || window.GPNFData.nonces.duplicate_entry || '')
+ : '';
return function( entryId ) {
- return $.post( gpnf.ajaxUrl, {
- action: 'gpnf_duplicate_entry',
- nonce: nonce,
- gpnf_entry_id: entryId,
- gpnf_parent_form_id: gpnf.formId,
- gpnf_nested_form_field_id: gpnf.fieldId,
- gpnf_context: gpnf.ajaxContext
- } ).then( function( response ) {
+ if ( ! nonce ) {
+ return $.Deferred().reject( 'Missing nonce for duplicate action.' );
+ }
+
+ return $.ajax( {
+ url: gpnf.ajaxUrl,
+ method: 'POST',
+ dataType: 'json',
+ timeout: 15000,
+ data: {
+ action: 'gpnf_duplicate_entry',
+ nonce: nonce,
+ gpnf_entry_id: entryId,
+ gpnf_parent_form_id: gpnf.formId,
+ gpnf_nested_form_field_id: gpnf.fieldId,
+ gpnf_context: gpnf.ajaxContext
+ }
+ } ).then( function( response ) {
if ( ! response || ! response.success ) {
return $.Deferred().reject( response && response.data ? response.data : 'Unable to duplicate entry.' );
}
- if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) {
- window.GPNestedForms.loadEntry( response.data );
- }
+ if ( gpnf && typeof gpnf.loadEntry === 'function' ) {
+ gpnf.loadEntry( response.data );
+ } else if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) {
+ window.GPNestedForms.loadEntry( response.data );
+ }
if ( window.gform && typeof window.gform.doAction === 'function' ) {
- window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response );
+ window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response, gpnf );
}
return response;
} );
};
})();
📝 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.
// Wrap GPNF's duplicate endpoint in a promise so we can queue requests sequentially. | |
var duplicateOnce = (function() { | |
var nonce = window.GPNFData && window.GPNFData.nonces ? window.GPNFData.nonces.duplicateEntry : ''; | |
return function( entryId ) { | |
return $.post( gpnf.ajaxUrl, { | |
action: 'gpnf_duplicate_entry', | |
nonce: nonce, | |
gpnf_entry_id: entryId, | |
gpnf_parent_form_id: gpnf.formId, | |
gpnf_nested_form_field_id: gpnf.fieldId, | |
gpnf_context: gpnf.ajaxContext | |
} ).then( function( response ) { | |
if ( ! response || ! response.success ) { | |
return $.Deferred().reject( response && response.data ? response.data : 'Unable to duplicate entry.' ); | |
} | |
if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) { | |
window.GPNestedForms.loadEntry( response.data ); | |
} | |
if ( window.gform && typeof window.gform.doAction === 'function' ) { | |
window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response ); | |
} | |
return response; | |
} ); | |
}; | |
})(); | |
// Wrap GPNF's duplicate endpoint in a promise so we can queue requests sequentially. | |
var duplicateOnce = (function() { | |
var nonce = window.GPNFData && window.GPNFData.nonces | |
? (window.GPNFData.nonces.duplicateEntry || window.GPNFData.nonces.duplicate_entry || '') | |
: ''; | |
return function( entryId ) { | |
if ( ! nonce ) { | |
return $.Deferred().reject( 'Missing nonce for duplicate action.' ); | |
} | |
return $.ajax( { | |
url: gpnf.ajaxUrl, | |
method: 'POST', | |
dataType: 'json', | |
timeout: 15000, | |
data: { | |
action: 'gpnf_duplicate_entry', | |
nonce: nonce, | |
gpnf_entry_id: entryId, | |
gpnf_parent_form_id: gpnf.formId, | |
gpnf_nested_form_field_id: gpnf.fieldId, | |
gpnf_context: gpnf.ajaxContext | |
} | |
} ).then( function( response ) { | |
if ( ! response || ! response.success ) { | |
return $.Deferred().reject( response && response.data ? response.data : 'Unable to duplicate entry.' ); | |
} | |
if ( gpnf && typeof gpnf.loadEntry === 'function' ) { | |
gpnf.loadEntry( response.data ); | |
} else if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) { | |
window.GPNestedForms.loadEntry( response.data ); | |
} | |
if ( window.gform && typeof window.gform.doAction === 'function' ) { | |
window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response, gpnf ); | |
} | |
return response; | |
} ); | |
}; | |
})(); |
🤖 Prompt for AI Agents
In gp-nested-forms/duplicate-child-entry-multiple-times.js around lines 44 to
72, the duplicateOnce wrapper should fail early if the nonce is missing, use the
instance method gpnf.loadEntry (not window.GPNestedForms.loadEntry), and make
the AJAX call more robust by specifying dataType and a timeout and by using
$.ajax so you can reject on network/error conditions; update the code to check
that nonce and gpnf exist before sending, call gpnf.loadEntry(response.data)
when available, use $.ajax({ url: gpnf.ajaxUrl, method: 'POST', dataType:
'json', timeout: 10000, data: { ... } }).then(...).catch(...) and reject with
the response.data or a clear error string on failure.
duplicate-child-entry-multiple-times.js
: Added new snippetgpnf-duplicate-child-entry-multiple-times.js
: Added new snippet
gpnf-duplicate-child-entry-multiple-times.js
: Added new snippetgpnf-duplicate-child-entry-multiple-times.js
: Added new snippet.
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: 0
♻️ Duplicate comments (1)
gp-nested-forms/gpnf-duplicate-child-entry-multiple-times.js (1)
44-72
: Fix: call instanceloadEntry
; add nonce guard; harden AJAX.Use the instance method (
gpnf.loadEntry
) rather than a global static, fail fast if the nonce is missing, and add a timeout/dataType for robustness.// Wrap GPNF's duplicate endpoint in a promise so we can queue requests sequentially. var duplicateOnce = (function() { - var nonce = window.GPNFData && window.GPNFData.nonces ? window.GPNFData.nonces.duplicateEntry : ''; + var nonce = window.GPNFData && window.GPNFData.nonces + ? (window.GPNFData.nonces.duplicateEntry || window.GPNFData.nonces.duplicate_entry || '') + : ''; return function( entryId ) { - return $.post( gpnf.ajaxUrl, { - action: 'gpnf_duplicate_entry', - nonce: nonce, - gpnf_entry_id: entryId, - gpnf_parent_form_id: gpnf.formId, - gpnf_nested_form_field_id: gpnf.fieldId, - gpnf_context: gpnf.ajaxContext - } ).then( function( response ) { + if ( ! nonce ) { + return $.Deferred().reject( 'Missing nonce for duplicate action.' ); + } + + return $.ajax( { + url: gpnf.ajaxUrl, + method: 'POST', + dataType: 'json', + timeout: 15000, + data: { + action: 'gpnf_duplicate_entry', + nonce: nonce, + gpnf_entry_id: entryId, + gpnf_parent_form_id: gpnf.formId, + gpnf_nested_form_field_id: gpnf.fieldId, + gpnf_context: gpnf.ajaxContext + } + } ).then( function( response ) { if ( ! response || ! response.success ) { return $.Deferred().reject( response && response.data ? response.data : 'Unable to duplicate entry.' ); } - if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) { - window.GPNestedForms.loadEntry( response.data ); - } + if ( gpnf && typeof gpnf.loadEntry === 'function' ) { + gpnf.loadEntry( response.data ); + } else if ( window.GPNestedForms && typeof window.GPNestedForms.loadEntry === 'function' ) { + window.GPNestedForms.loadEntry( response.data ); + } if ( window.gform && typeof window.gform.doAction === 'function' ) { - window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response ); + window.gform.doAction( 'gpnf_post_duplicate_entry', response.data.entry, response, gpnf ); } return response; } ); }; })();
🧹 Nitpick comments (5)
gp-nested-forms/gpnf-duplicate-child-entry-multiple-times.js (5)
76-77
: Consider improving user experience for input prompts.The native
window.prompt
provides limited UX and may be blocked by browsers. Consider using a custom modal dialog for better user experience and validation feedback.Would you like me to provide an implementation using a custom modal dialog that offers better validation feedback and a more polished user experience?
86-87
: Add validation message when default value is applied.When the user enters an invalid value (non-numeric or less than 1), the code silently defaults to 1 without informing the user. Consider showing a notification.
if ( isNaN( copies ) || copies < 1 ) { + console.warn( 'Invalid input. Defaulting to 1 copy.' ); copies = 1; }
108-110
: Add user feedback when reaching entry limit.When the entry limit prevents duplication (copies < 1), the function returns silently. Users might be confused why nothing happened.
if ( copies < 1 ) { + if ( max !== '' && max != null ) { + window.alert( 'Entry limit reached. No additional copies can be created.' ); + } return; }
119-123
: Consider adding progress indication for multiple duplications.When duplicating many entries, users have no visibility into the progress. Consider adding a progress indicator for better UX, especially for larger copy counts.
Would you like me to provide an implementation that displays a progress indicator during the duplication process?
157-161
: Consider defensive programming for global object iteration.Iterating over all global window properties could potentially process unexpected objects. Consider adding additional validation to ensure the objects are GPNF instances.
for ( var key in window ) { if ( key.indexOf( 'GPNestedForms_' ) === 0 ) { - init( window[ key ] ); + var obj = window[ key ]; + if ( obj && typeof obj === 'object' && typeof obj.fieldId === 'number' ) { + init( obj ); + } } }
Context
⛑️ Ticket(s): https://secure.helpscout.net/conversation/3072037627/89219?viewId=8172236
Summary
This snippet lets users duplicate a child entry multiple times in one go: using the snippet, clicking “Duplicate” now prompts for a quantity, applies any
maxCopies
setting and field entry limit during validation, then runs thegpnf_duplicate_entry
AJAX request that many times in sequence.Loom video showing the snippet coming soon.