From c81a0ef5a1b865e7f9e755828e03e360dcff5a9d Mon Sep 17 00:00:00 2001 From: kodinkat Date: Wed, 17 May 2023 12:36:51 +0100 Subject: [PATCH] WP Admin: Fixed Issue Saving Field Options (#2067) * Fixed issue custom field option translations not being saved * Updated to ensure all translations are now handled within same logical ajax update flow * Added display translation dialog source flag * Refactored custom fields tab save flow --- dt-core/admin/js/dt-options.js | 168 +++++++++++++++++- dt-core/admin/menu/tabs/admin-endpoints.php | 86 +++++++++ dt-core/admin/menu/tabs/tab-custom-fields.php | 90 +++------- dt-core/admin/menu/tabs/tab-custom-lists.php | 15 +- dt-core/admin/menu/tabs/tab-custom-tiles.php | 4 +- 5 files changed, 289 insertions(+), 74 deletions(-) diff --git a/dt-core/admin/js/dt-options.js b/dt-core/admin/js/dt-options.js index 9544f5c0ca..52ef4b1a6f 100644 --- a/dt-core/admin/js/dt-options.js +++ b/dt-core/admin/js/dt-options.js @@ -1,7 +1,7 @@ jQuery(document).ready(function ($) { - $('.expand_translations').click(function () { - event.preventDefault(); - display_translation_dialog($(this).siblings(), $(this).data('form_name')); + $('.expand_translations').click(function (e) { + e.preventDefault(); + display_translation_dialog($(this).siblings(), $(this).data('form_name'), $(this).data('source')); }); $('.change-icon-button').click(function (e) { @@ -42,12 +42,12 @@ jQuery(document).ready(function ($) { * Translation modal dialog */ - function display_translation_dialog(container, form_name) { + function display_translation_dialog(container, form_name, source = '') { let dialog = $('#dt_translation_dialog'); if (container && form_name && dialog) { // Update dialog div - $(dialog).empty().append($(container).find('table').clone()); + $(dialog).empty().append($($(container).find('table')[0]).clone()); // Refresh dialog config dialog.dialog({ @@ -68,8 +68,12 @@ jQuery(document).ready(function ($) { // Close dialog $(this).dialog('close'); - // Finally, auto save changes - $('form[name="' + form_name + '"]').submit(); + // Finally, auto save changes, accordingly, based on source. + if (window.lodash.includes(['fields'], source)) { + handle_custom_field_save_request(null, $('.dt-custom-fields-save-button')[0], true); + } else { + $('form[name="' + form_name + '"]').submit(); + } } } @@ -558,4 +562,154 @@ jQuery(document).ready(function ($) { /** * Tile Display Help Modal - [END] */ + + /** + * Alternative Save Flow - [START] + */ + + $(document).on('click', '.dt-custom-fields-save-button', function (e) { + handle_custom_field_save_request(e, $(e.currentTarget), false); + }); + + function handle_custom_field_save_request(event, save_button, translate_update_only) { + + // If defined, short-circuit default save flow and adopt ajax approach if needed. + if (event) { + event.preventDefault(); + } + + // Determine which save path is to be taken. + if (!translate_update_only) { + $('form[name="' + $(save_button).data('form_id') + '"]').submit(); + } else { + + // Always capture field parent level name & description translations; which is present across all fields. + let payload = { + 'post_type': $(save_button).data('post_type'), + 'field_id': $(save_button).data('field_id'), + 'field_type': $(save_button).data('field_type'), + 'translations': package_custom_field_translations($(save_button).data('field_id')), + 'option_translations': window.lodash.includes(['key_select', 'multi_select', 'link'], $(save_button).data('field_type')) ? package_custom_field_option_translations():[] + }; + + // Have core endpoint process field translations accordingly. + $.ajax({ + type: 'POST', + contentType: 'application/json; charset=utf-8', + dataType: 'json', + data: JSON.stringify(payload), + url: `${window.dt_admin_scripts.rest_root}dt-admin/scripts/update_custom_field_translations`, + beforeSend: (xhr) => { + xhr.setRequestHeader('X-WP-Nonce', window.dt_admin_scripts.nonce); + } + }).done(function (response) { + console.log(response); + + // Update translation counts. + $('#custom_name_translation_count').html((response['translations']) ? Object.keys(response['translations']).length:0); + $('#custom_description_translation_count').html((response['description_translations']) ? Object.keys(response['description_translations']).length:0); + if ((response['defaults']) && window.lodash.includes(['key_select', 'multi_select', 'link'], $(save_button).data('field_type'))) { + $('.sortable-field-options').find('tr.ui-sortable-handle').each(function (idx, tr) { + let option_key = $(tr).find('.sortable-field-options-key').text().trim(); + $(tr).find('#option_name_translation_count').html((response['defaults'] && response['defaults'][option_key] && response['defaults'][option_key]['translations']) ? Object.keys(response['defaults'][option_key]['translations']).length:0); + $(tr).find('#option_description_translation_count').html((response['defaults'] && response['defaults'][option_key] && response['defaults'][option_key]['description_translations']) ? Object.keys(response['defaults'][option_key]['description_translations']).length:0); + }); + } + + }).fail(function (error) { + console.log("error"); + console.log(error); + }); + } + } + + function package_custom_field_translations(field_id) { + let packaged_translations = { + 'translations': [], + 'description_translations': [] + }; + + // Locate field name translations. + let field_name_prefix = 'field_key_' + field_id + '_translation-'; + $("input[id^='" + field_name_prefix + "']").each(function (idx, input) { + let locale = window.lodash.split($(input).attr('id'), '-')[1]; + let value = $(input).val(); + if (locale && value) { + packaged_translations['translations'].push({ + 'locale': locale, + 'value': value + }); + } + }); + + // Locate field description translations. + let field_description_prefix = 'field_description_translation-'; + $("input[id^='" + field_description_prefix + "']").each(function (idx, input) { + let locale = window.lodash.split($(input).attr('id'), '-')[1]; + let value = $(input).val(); + if (locale && value) { + packaged_translations['description_translations'].push({ + 'locale': locale, + 'value': value + }); + } + }); + + return packaged_translations; + } + + function package_custom_field_option_translations() { + let packaged_translations = []; + + $('.sortable-field-options').find('tr.ui-sortable-handle').each(function (idx, tr) { + let translations = { + 'option_key': '', + 'option_translations': [], + 'option_description_translations': [] + }; + + // Determine option key. + let option_key = $(tr).find('.sortable-field-options-key').text().trim(); + if (option_key) { + translations['option_key'] = option_key; + + // Locate option key translations. + let option_key_prefix = 'field_option_' + option_key + '_translation-'; + $(tr).find("input[id^='" + option_key_prefix + "']").each(function (okt_idx, okt_input) { + let locale = window.lodash.split($(okt_input).attr('id'), '-')[1]; + let value = $(okt_input).val(); + if (locale && value) { + translations['option_translations'].push({ + 'locale': locale, + 'value': $(okt_input).val() + }); + } + }); + + // Locate option key description translations. + let option_key_description_prefix = 'option_description_' + option_key + '_translation-'; + $(tr).find("input[id^='" + option_key_description_prefix + "']").each(function (okdt_idx, okdt_input) { + let locale = window.lodash.split($(okdt_input).attr('id'), '-')[1]; + let value = $(okdt_input).val(); + if (locale && value) { + translations['option_description_translations'].push({ + 'locale': locale, + 'value': $(okdt_input).val() + }); + } + }); + + // Package recent translations. + packaged_translations.push(translations); + } + }); + + return packaged_translations; + } + + /** + * Alternative Save Flow - [END] + */ + + }) diff --git a/dt-core/admin/menu/tabs/admin-endpoints.php b/dt-core/admin/menu/tabs/admin-endpoints.php index b720b5a250..2815849f44 100644 --- a/dt-core/admin/menu/tabs/admin-endpoints.php +++ b/dt-core/admin/menu/tabs/admin-endpoints.php @@ -25,6 +25,15 @@ public function add_api_routes(){ }, ] ); + register_rest_route( + $this->namespace, '/scripts/update_custom_field_translations', [ + 'methods' => 'POST', + 'callback' => [ $this, 'update_custom_field_translations' ], + 'permission_callback' => function(){ + return current_user_can( 'manage_dt' ); + }, + ] + ); } public function reset_count_field( WP_REST_Request $request ){ @@ -61,6 +70,83 @@ public function reset_count_field_progress( WP_REST_Request $request ){ } } + public function update_custom_field_translations( WP_REST_Request $request ){ + $params = $request->get_params(); + if ( isset( $params['post_type'], $params['field_id'], $params['field_type'], $params['translations'] ) ){ + $post_type = $params['post_type']; + $field_id = $params['field_id']; + $field_type = $params['field_type']; + $translations = $params['translations']; + $option_translations = $params['option_translations'] ?? []; + + // Fetch existing field customizations and if needed, create relevant spaces! + $field_customizations = dt_get_option( 'dt_field_customizations' ); + if ( !isset( $field_customizations[$post_type][$field_id] ) ){ + $field_customizations[$post_type][$field_id] = []; + } + $custom_field = $field_customizations[$post_type][$field_id]; + + // Capture available field name translations. + $custom_field['translations'] = []; + foreach ( $translations['translations'] ?? [] as $translation ){ + if ( !empty( $translation['locale'] ) && !empty( $translation['value'] ) ){ + $custom_field['translations'][$translation['locale']] = $translation['value']; + } + } + + // Capture available field description translations. + $custom_field['description_translations'] = []; + foreach ( $translations['description_translations'] ?? [] as $translation ){ + if ( !empty( $translation['locale'] ) && !empty( $translation['value'] ) ){ + $custom_field['description_translations'][$translation['locale']] = $translation['value']; + } + } + + // If required, update custom field default options. + if ( in_array( $field_type, [ 'key_select', 'multi_select', 'link' ] ) ){ + $defaults = []; + foreach ( $option_translations as $option ){ + $option_key = $option['option_key']; + + if ( !empty( $option_key ) ){ + $defaults[$option_key] = []; + $defaults[$option_key]['translations'] = []; + $defaults[$option_key]['description_translations'] = []; + + // Capture option translations. + foreach ( $option['option_translations'] ?? [] as $option_translation ){ + if ( !empty( $option_translation['locale'] ) && !empty( $option_translation['value'] ) ){ + $defaults[$option_key]['translations'][$option_translation['locale']] = $option_translation['value']; + } + } + + // Capture option description translations. + foreach ( $option['option_description_translations'] ?? [] as $option_description_translations ){ + if ( !empty( $option_description_translations['locale'] ) && !empty( $option_description_translations['value'] ) ){ + $defaults[$option_key]['description_translations'][$option_description_translations['locale']] = $option_description_translations['value']; + } + } + } + } + $custom_field['default'] = $defaults; + } + + // Persist updated custom field option translations. + $field_customizations[$post_type][$field_id] = $custom_field; + update_option( 'dt_field_customizations', $field_customizations ); + + // For completeness, return updated shape! + return [ + 'translations' => $custom_field['translations'] ?? [], + 'description_translations' => $custom_field['description_translations'] ?? [], + 'defaults' => $custom_field['default'] ?? [] + ]; + + } else { + return new WP_Error( __FILE__, 'Missing required parameters.' ); + } + } + } use WP_Queue\Job; diff --git a/dt-core/admin/menu/tabs/tab-custom-fields.php b/dt-core/admin/menu/tabs/tab-custom-fields.php index eeebb5d345..e454c83b75 100644 --- a/dt-core/admin/menu/tabs/tab-custom-fields.php +++ b/dt-core/admin/menu/tabs/tab-custom-fields.php @@ -597,7 +597,8 @@ private function edit_field( $field_key, $post_type ){
@@ -620,7 +621,7 @@ private function edit_field( $field_key, $post_type ){ for="field_key__translation-"> @@ -669,7 +670,8 @@ private function edit_field( $field_key, $post_type ){
@@ -691,7 +693,7 @@ private function edit_field( $field_key, $post_type ){ for="field_description_translation-"> @@ -739,7 +741,7 @@ private function edit_field( $field_key, $post_type ){
- +
- + @@ -934,7 +936,9 @@ class="color-display-picker" data-alpha-enabled="true"
-
$val ) : ?> - +
@@ -968,7 +972,9 @@ class="color-display-picker" data-alpha-enabled="true"
-
$val ) : ?> - +
@@ -1016,7 +1022,7 @@ class="color-display-picker" data-alpha-enabled="true" value="[]"/>
- + @@ -1026,9 +1032,10 @@ class="color-display-picker" data-alpha-enabled="true" /** * Visibility controls */ + $field_extras_form_name = 'field_extras_form'; ?> -
+ @@ -1048,7 +1055,7 @@ class="color-display-picker" data-alpha-enabled="true"

- +

@@ -1066,7 +1073,7 @@ class="color-display-picker" data-alpha-enabled="true"

- +
$val ){ - $langcode = $val['language']; - $translation_key = 'field_key_' . $field_key . '_translation-' . $langcode; - if ( isset( $post_submission[$translation_key] ) ) { - if ( empty( $post_submission[$translation_key] ) && isset( $custom_field['translations'][$langcode] ) ){ - unset( $post_submission[$translation_key] ); - } elseif ( !empty( $post_submission[$translation_key] ) ) { - $custom_field['translations'][$langcode] = $post_submission[$translation_key]; - } - } - } } if ( isset( $post_submission['delete_custom_label'], $custom_field['name'] ) ){ unset( $custom_field['name'] ); @@ -1181,18 +1177,6 @@ private function process_edit_field( $post_submission ){ if ( isset( $post_submission['field_description'] ) && $post_submission['field_description'] != ( $custom_field['description'] ?? '' ) ){ $custom_field['description'] = $post_submission['field_description']; } - //field description translation - foreach ( $post_submission as $key => $val ){ - if ( strpos( $key, 'field_description_translation' ) === 0 ){ - $language_code = substr( $key, strlen( 'field_description_translation-' ) ); - if ( ( !isset( $custom_field['description_translations'] ) || empty( $custom_field['description_translations'] ) ) && !empty( $val ) ){ - $custom_field['description_translations'][$language_code] = $val; - } - if ( empty( $val ) && isset( $custom_field['description_translations'][$language_code] ) ){ - unset( $custom_field['description_translations'][$language_code] ); - } - } - } //field tile if ( isset( $post_submission['tile_select'] ) ){ @@ -1222,22 +1206,13 @@ private function process_edit_field( $post_submission ){ } } - // key_select and multi_options + // key_select, multi_options & links if ( isset( $post_fields[$field_key]['default'] ) && ( $field['type'] === 'multi_select' || $field['type'] === 'key_select' || $field['type'] === 'link' ) ){ $field_options = $field['default']; foreach ( $post_submission as $key => $val ){ if ( strpos( $key, 'field_option_' ) === 0 ) { if ( strpos( $key, 'translation' ) !== false ) { - $option_key = substr( $key, 13, strpos( $key, 'translation' ) - 14 ); - $translation_langcode = substr( $key, strpos( $key, 'translation' ) + strlen( 'translation-' ) ); - if ( strpos( $translation_langcode, '-' ) !== false ) { - $translation_langcode = substr( $translation_langcode, 3 ); - } - if ( empty( $val ) && isset( $custom_field['default'][ $option_key ]['translations'][ $translation_langcode ] ) ) { - unset( $custom_field['default'][ $option_key ]['translations'][ $translation_langcode ] ); - } elseif ( ! empty( $val ) ) { - $custom_field['default'][ $option_key ]['translations'][ $translation_langcode ] = $val; - } + continue; } elseif ( strpos( $key, 'icon' ) !== false ) { $option_key = substr( $key, 18 ); @@ -1268,16 +1243,7 @@ private function process_edit_field( $post_submission ){ if ( strpos( $key, 'option_description_' ) === 0 ) { if ( strpos( $key, 'translation' ) !== false ) { - $option_key = substr( $key, strlen( 'option_description_' ), strpos( $key, 'translation' ) - ( strlen( 'option_description_' ) + 1 ) ); - $translation_langcode = substr( $key, strpos( $key, 'translation' ) + strlen( 'translation-' ) ); - if ( strpos( $translation_langcode, '-' ) !== false ) { - $translation_langcode = substr( $translation_langcode, 3 ); - } - if ( empty( $val ) && isset( $custom_field['default'][$option_key]['description_translations'][$translation_langcode] ) ){ - unset( $custom_field['default'][$option_key]['description_translations'][$translation_langcode] ); - } elseif ( !empty( $val ) ) { - $custom_field['default'][$option_key]['description_translations'][$translation_langcode] = $val; - } + continue; } else { $option_key = substr( $key, strlen( 'option_description_' ) ); //if the description isn't set and the value is not empty, or if the value changed. diff --git a/dt-core/admin/menu/tabs/tab-custom-lists.php b/dt-core/admin/menu/tabs/tab-custom-lists.php index bd0d187b4a..baee73025d 100644 --- a/dt-core/admin/menu/tabs/tab-custom-lists.php +++ b/dt-core/admin/menu/tabs/tab-custom-lists.php @@ -369,7 +369,8 @@ public function comment_types_box() {
- - - -