-
Notifications
You must be signed in to change notification settings - Fork 41
Try to fix unique id issue #3263
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -255,6 +255,7 @@ window.frmAdminBuildJS = function() { | |
| let fieldsUpdated = 0; | ||
| let thisFormId = 0; | ||
| let autoId = 0; | ||
| let nextFieldOrder = 0; | ||
| const optionMap = {}; | ||
| let lastNewActionIdReturned = 0; | ||
|
|
||
|
|
@@ -1860,6 +1861,7 @@ window.frmAdminBuildJS = function() { | |
| * Get the arguments for inserting a new field. | ||
| * | ||
| * @since 6.23 | ||
| * @since x.x A field_order is reserved for the new field. | ||
| * | ||
| * @param {string} fieldType The type of field to insert. | ||
| * @param {string} sectionId The section ID to insert into. | ||
|
|
@@ -1884,6 +1886,8 @@ window.frmAdminBuildJS = function() { | |
| fieldArgs.last_row_field_ids = getFieldIdsInSubmitRow(); | ||
| } | ||
|
|
||
| fieldArgs.field_order = reserveFieldOrder( isInRepeater ? 0 : fieldArgs.last_row_field_ids.length ); | ||
|
|
||
|
Comment on lines
+1889
to
+1890
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win Synchronize the counter after sidebar insertion. Line 1889 reserves a field order for the sidebar insertion path. The successful sidebar handler does not call Proposed fix success( msg ) {
+ syncFieldOrderWithResponse( msg );
handleAddFieldClickResponse( msg );🤖 Prompt for AI Agents |
||
| return fieldArgs; | ||
| } | ||
|
|
||
|
|
@@ -1940,6 +1944,7 @@ window.frmAdminBuildJS = function() { | |
| url: ajaxurl, | ||
| data: getInsertNewFieldArgs( fieldType, sectionId, formId, hasBreak ), | ||
| success( msg ) { | ||
| syncFieldOrderWithResponse( msg ); | ||
| handleInsertFieldByDraggingResponse( msg, $placeholder ); | ||
|
|
||
| const fieldId = checkMsgForFieldId( msg ); | ||
|
|
@@ -2064,6 +2069,76 @@ window.frmAdminBuildJS = function() { | |
| return ++autoId; | ||
| } | ||
|
|
||
| /** | ||
| * Reserve a unique field_order for a field that is about to be created. | ||
| * | ||
| * The server cannot allocate this on its own. It reads the current highest | ||
| * field_order and inserts the row in two separate queries, so requests that | ||
| * overlap, which is what happens when fields are dragged in faster than the | ||
| * requests come back, all read the same value and end up sharing an order. | ||
| * Fields are then sorted on an ambiguous field_order and swap places between | ||
| * page loads. JavaScript is single threaded, so a counter here can never hand | ||
| * out the same value twice. | ||
| * | ||
| * @since x.x | ||
| * | ||
| * @param {number} lastRowFieldCount How many fields in the submit row the server | ||
| * will push after the new one. | ||
| * @return {number} The reserved field order, or 0 to let the server pick one. | ||
| */ | ||
| function reserveFieldOrder( lastRowFieldCount ) { | ||
| if ( ! nextFieldOrder ) { | ||
| // There is nothing to count up from yet, so let the server allocate the | ||
| // order the way it always has rather than risk handing out one that is | ||
| // already taken. The first response seeds the counter for the rest. | ||
| return 0; | ||
| } | ||
|
|
||
| const reserved = ++nextFieldOrder; | ||
|
|
||
| // The submit row is moved to the orders directly after the new field, so | ||
| // step over those as well to leave the next reservation somewhere free. | ||
| nextFieldOrder += lastRowFieldCount; | ||
|
|
||
| return reserved; | ||
| } | ||
|
|
||
| /** | ||
| * Catch the counter up to the orders the server actually used. | ||
| * | ||
| * One insert can move or create more than one field. A section also gets an end | ||
| * divider, a summary field can add a page break, and the submit row is pushed | ||
| * along behind them. Those orders are all picked server side, where they are | ||
| * still read from the database, so they can land above the counter. The response | ||
| * reports every one of them, which makes it the authority on what was used. | ||
| * | ||
| * @since x.x | ||
| * | ||
| * @param {string} html The field HTML returned by the insert request. | ||
| * @return {void} | ||
| */ | ||
| function syncFieldOrderWithResponse( html ) { | ||
| const wrapper = div(); | ||
| wrapper.innerHTML = html; | ||
|
|
||
| const orders = []; | ||
|
|
||
| wrapper.querySelectorAll( 'input[name^="field_options[field_order_"]' ).forEach( | ||
| input => orders.push( parseInt( input.value, 10 ) ) | ||
| ); | ||
|
|
||
| const lastRowOrderInput = wrapper.querySelector( '#frm-last-row-fields-order' ); | ||
| if ( lastRowOrderInput ) { | ||
| Object.values( JSON.parse( lastRowOrderInput.value ) ).forEach( | ||
| order => orders.push( parseInt( order, 10 ) ) | ||
| ); | ||
| } | ||
|
|
||
| if ( orders.length ) { | ||
| nextFieldOrder = Math.max( nextFieldOrder, ...orders ); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Determine if a draggable element can be droppable into a droppable element. | ||
| * | ||
|
|
@@ -2509,6 +2584,7 @@ window.frmAdminBuildJS = function() { | |
| url: ajaxurl, | ||
| data: Object.assign( getInsertNewFieldArgs( fieldType, 0, formId, hasBreak ), { field_options: fieldOptions } ), | ||
| success( msg ) { | ||
| syncFieldOrderWithResponse( msg ); | ||
| resolve( msg ); | ||
|
|
||
| setTimeout( () => { | ||
|
|
@@ -11046,6 +11122,7 @@ window.frmAdminBuildJS = function() { | |
| debouncedSyncAfterDragAndDrop = debounce( syncAfterDragAndDrop, 10 ); | ||
| postBodyContent = document.getElementById( 'post-body-content' ); | ||
| $postBodyContent = jQuery( postBodyContent ); | ||
| nextFieldOrder = parseInt( $newFields[ 0 ].dataset.nextFieldOrder, 10 ) || 0; | ||
|
|
||
| if ( jQuery( '.frm_field_loading' ).length ) { | ||
| loadFieldId = jQuery( '.frm_field_loading' ).first().attr( 'id' ); | ||
|
|
||
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.
A variable has been used but not defined, which may result in warnings during program execution. This can also cause bugs since the intended usage scope of the variable is not known.