| @@ -1,193 +1,121 @@ | ||
| 1 | -<?php | |
| 1 | +<?php | |
| 2 | +/** | |
| 3 | + * Read-only renderer for the Field Configuration settings tab. | |
| 4 | + * | |
| 5 | + * Metadata gathering owns initialization and persistence. This partial only | |
| 6 | + * checks connection state, asks the deep module for an in-memory normalized | |
| 7 | + * view, and renders the active fields. There is deliberately no settings form, | |
| 8 | + * options.php submission, pagination state, or browser-triggered initial save; | |
| 9 | + * the JavaScript controller sends compact commands to the single AJAX seam. | |
| 10 | + * | |
| 11 | + * PER-CONNECTION SCOPE (multi-MLS): the Field Configuration is stored per | |
| 12 | + * connection (#275). This tab addresses ONE connection, resolved by the shared | |
| 13 | + * scope rule (includes/mlsimport-field-mapping-scope.php): ?mls=<id> when it | |
| 14 | + * names a registered connection, the current connection otherwise. With 2+ | |
| 15 | + * connections a selector at the top switches the scope by reloading the page; | |
| 16 | + * the resolved scope is exposed to the JS in the hidden #mlsimport_field_scope | |
| 17 | + * input so every mutation and gather request stays on the same connection. | |
| 18 | + * | |
| 19 | + * Step by step: | |
| 20 | + * 1. Resolve the scope and read that connection's populated flag. | |
| 21 | + * 2. Gate on the global SaaS account token (the account is install-wide). | |
| 22 | + * 3. Gate on the MLS connection: the CURRENT connection keeps the historic | |
| 23 | + * global flag + refresh flow; a non-current scope uses its own record's | |
| 24 | + * tested status (the global flag belongs to the current connection only). | |
| 25 | + * 4. With 2+ connections, render the scope selector. | |
| 26 | + * 5. Render the scoped configuration, or the "gathering" notice when the | |
| 27 | + * scope's metadata is not populated yet. | |
| 28 | + * | |
| 29 | + * @package MLSImport | |
| 30 | + * @subpackage MLSImport/admin/partials | |
| 31 | + */ | |
| 32 | + | |
| 2 | 33 | if ( ! defined( 'ABSPATH' ) ) { |
| 3 | - exit; // Exit if accessed directly | |
| 34 | + exit; | |
| 4 | 35 | } |
| 5 | -$theme_schema= mlsimport_hardocde_theme_schema(); | |
| 6 | 36 | |
| 7 | -$options = get_option( 'mlsimport_admin_options' ); | |
| 8 | -$mlsimport_mls_metadata_populated = get_option( 'mlsimport_mls_metadata_populated', '' ); | |
| 9 | -$permited_tags = mlsimport_allowed_html_tags_content(); | |
| 10 | -$post_type = ''; | |
| 11 | -if (method_exists($this->env_data, 'get_property_post_type')) { | |
| 12 | - $post_type = $this->env_data->get_property_post_type(); | |
| 13 | -} | |
| 37 | +global $mlsimport; | |
| 14 | 38 | |
| 15 | -$available_taxonomies = mlsimport_get_custom_post_type_taxonomies($post_type); | |
| 39 | +// Step 1: which connection is this tab editing? (?mls=<registered id> wins, | |
| 40 | +// anything else resolves to the current connection.) | |
| 41 | +$mlsimport_field_scope = mlsimport_field_mapping_request_scope( isset( $_GET['mls'] ) ? wp_unslash( $_GET['mls'] ) : null ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only view selector. | |
| 42 | +$mlsimport_connections = Mlsimport_Connections::all(); | |
| 43 | +$mlsimport_scope_is_current = $mlsimport_field_scope === mlsimport_current_mls_id(); | |
| 16 | 44 | |
| 45 | +$metadata_populated = mlsimport_get_connection_option( 'mlsimport_mls_metadata_populated', '', $mlsimport_field_scope ); | |
| 46 | +$token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient(); | |
| 17 | 47 | |
| 18 | -global $mlsimport; | |
| 19 | -$token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient(); | |
| 20 | -$is_mls_connected = get_option('mlsimport_connection_test', ''); | |
| 48 | +// Step 2: no SaaS account token — nothing on this tab can work. | |
| 21 | 49 | $mlsimport->admin->mlsimport_saas_setting_up(); |
| 50 | +if ( '' === trim( $token ) ) { | |
| 51 | + // Names the reason (no subscription vs wrong password). | |
| 52 | + echo mlsimport_account_not_connected_html(); // phpcs:ignore WordPress.Security.EscapeOutput -- escaped by the builder. | |
| 53 | + return; | |
| 54 | +} | |
| 22 | 55 | |
| 23 | -if ('yes' !== $is_mls_connected) { | |
| 24 | - $mlsimport->admin->mlsimport_saas_check_mls_connection(); | |
| 25 | - $is_mls_connected = get_option('mlsimport_connection_test', ''); | |
| 56 | +// Step 3: the MLS-connected gate, per scope. | |
| 57 | +if ( $mlsimport_scope_is_current ) { | |
| 58 | + // Historic flow: refresh the global connection state before deciding. | |
| 59 | + $is_mls_connected = get_option( 'mlsimport_connection_test', '' ); | |
| 60 | + if ( 'yes' !== $is_mls_connected ) { | |
| 61 | + $mlsimport->admin->mlsimport_saas_check_mls_connection(); | |
| 62 | + $is_mls_connected = get_option( 'mlsimport_connection_test', '' ); | |
| 63 | + } | |
| 64 | +} else { | |
| 65 | + // A non-current connection answers from its own record (#277 status). | |
| 66 | + $mlsimport_scope_record = Mlsimport_Connections::get( $mlsimport_field_scope ); | |
| 67 | + $is_mls_connected = is_array( $mlsimport_scope_record ) ? (string) ( $mlsimport_scope_record['status'] ?? '' ) : ''; | |
| 26 | 68 | } |
| 27 | 69 | |
| 28 | -if (trim($token) === '') { | |
| 29 | - echo '<div class="mlsimport_warning">' . esc_html__('You are not connected to MlsImport - Please check your Username and Password.', 'mlsimport') . '</div>'; | |
| 30 | - return; | |
| 70 | +if ( 'yes' !== $is_mls_connected ) { | |
| 71 | + echo '<div class="mlsimport_warning">' . esc_html__( 'The connection to your MLS was NOT succesful. Please check the authentication token is correct and check your MLS Data Access Application is approved.', 'mlsimport' ) . '</div>'; | |
| 72 | + return; | |
| 31 | 73 | } |
| 32 | 74 | |
| 33 | -if ('yes' !== $is_mls_connected) { | |
| 34 | - echo '<div class="mlsimport_warning">' . esc_html__('The connection to your MLS was NOT succesful. Please check the authentication token is correct and check your MLS Data Access Application is approved.', 'mlsimport') . '</div>'; | |
| 35 | - return; | |
| 75 | +// Step 4: with 2+ connections the user picks which MLS's fields to edit. | |
| 76 | +if ( count( $mlsimport_connections ) > 1 ) { | |
| 77 | + echo '<div class="mlsimport-field-scope">'; | |
| 78 | + echo '<label for="mlsimport-field-mls-scope">' . esc_html__( 'Editing fields for', 'mlsimport' ) . '</label> '; | |
| 79 | + echo '<select id="mlsimport-field-mls-scope">'; | |
| 80 | + foreach ( $mlsimport_connections as $mlsimport_scope_option ) { | |
| 81 | + $mlsimport_scope_id = (int) $mlsimport_scope_option['mls_id']; | |
| 82 | + $mlsimport_scope_label = '' !== $mlsimport_scope_option['mls_name'] | |
| 83 | + ? $mlsimport_scope_option['mls_name'] | |
| 84 | + : __( 'MLS', 'mlsimport' ) . ' ' . $mlsimport_scope_id; | |
| 85 | + printf( | |
| 86 | + '<option value="%d"%s>%s</option>', | |
| 87 | + esc_attr( $mlsimport_scope_id ), | |
| 88 | + selected( $mlsimport_field_scope, $mlsimport_scope_id, false ), | |
| 89 | + esc_html( $mlsimport_scope_label ) | |
| 90 | + ); | |
| 91 | + } | |
| 92 | + echo '</select>'; | |
| 93 | + echo '</div>'; | |
| 36 | 94 | } |
| 37 | 95 | |
| 38 | -if ( 'yes' === $mlsimport_mls_metadata_populated ) { | |
| 39 | - // We have MLS metadata, so we can show the field selection interface | |
| 40 | - ?> | |
| 41 | - <form method="post" class= "mlsimport-import-fields-form" name="cleanup_options" action="options.php"> | |
| 42 | - <?php | |
| 43 | - | |
| 44 | - | |
| 45 | - global $mlsimport; | |
| 46 | - $mlsimport->admin->mlsimport_saas_setting_up(); | |
| 47 | - | |
| 48 | - | |
| 49 | - // Add this to the beginning of the form processing | |
| 50 | - $options = get_option('mlsimport_admin_fields_select', array()); | |
| 51 | - | |
| 52 | - | |
| 53 | - // Ensure all arrays are initialized | |
| 54 | - if (!is_array($options) || | |
| 55 | - (is_array($options) && empty($options)) ) { | |
| 96 | +// Step 5: render the scoped configuration (or the gathering notice). | |
| 97 | +if ( 'yes' === $metadata_populated ) { | |
| 98 | + $metadata = mlsimport_field_configuration_metadata( $mlsimport_field_scope ); | |
| 99 | + $theme_schema = mlsimport_hardocde_theme_schema(); | |
| 100 | + $configuration = mlsimport_field_configuration( $mlsimport_field_scope )->read( $metadata, $theme_schema, mlsimport_field_configuration_taxonomies() ); | |
| 56 | 101 | |
| 57 | - $mlsimport_mls_metadata_mls_data = get_option( 'mlsimport_mls_metadata_mls_data', '' ); | |
| 58 | - $metadata_api_call_data_service_property = json_decode( $mlsimport_mls_metadata_mls_data, true ); | |
| 59 | - $options = array(); | |
| 60 | - | |
| 61 | - | |
| 62 | - | |
| 63 | - if (!isset($options['mls-fields'])) { | |
| 64 | - $options['mls-fields'] = array(); | |
| 65 | - } | |
| 66 | - | |
| 67 | - if (!isset($options['mls-fields-admin'])) { | |
| 68 | - $options['mls-fields-admin'] = array(); | |
| 69 | - } | |
| 70 | - | |
| 71 | - if (!isset($options['mls-fields-label'])) { | |
| 72 | - $options['mls-fields-label'] = array(); | |
| 73 | - } | |
| 74 | - | |
| 75 | - if (!isset($options['mls-fields-map-postmeta'])) { | |
| 76 | - $options['mls-fields-map-postmeta'] = array(); | |
| 77 | - } | |
| 78 | - | |
| 79 | - if (!isset($options['mls-fields-map-taxonomy'])) { | |
| 80 | - $options['mls-fields-map-taxonomy'] = array(); | |
| 81 | - } | |
| 82 | - | |
| 83 | - | |
| 84 | - $order_item=0; | |
| 85 | - if (!is_array($metadata_api_call_data_service_property)) { | |
| 86 | - $metadata_api_call_data_service_property = []; | |
| 87 | - } | |
| 88 | - | |
| 89 | - ksort($metadata_api_call_data_service_property); | |
| 90 | - | |
| 91 | - foreach ( $metadata_api_call_data_service_property as $key => $value ) { | |
| 92 | - $description = 'no description '; | |
| 93 | - | |
| 94 | - $options['mls-fields'][ $key ]=0; | |
| 95 | - $options['field_order'][ $key ]=$order_item++; | |
| 96 | - $options['mls-fields-admin'][ $key ]=0 ; | |
| 97 | - $options['mls-fields-map-postmeta'][ $key ]=''; | |
| 98 | - | |
| 99 | - $options['mls-fields-label'][ $key ]=''; | |
| 100 | - | |
| 101 | - if ( array_key_exists( $key, $theme_schema ) ) { | |
| 102 | - $options['mls-fields'][ $key ]=1; | |
| 103 | - | |
| 104 | - if( isset( $theme_schema[$key]['type']) && $theme_schema[$key]['type']=='taxonomy' ){ | |
| 105 | - $options['mls-fields-map-taxonomy'][ $key ]=$theme_schema[$key]['name']; | |
| 106 | - }else{ | |
| 107 | - $options['mls-fields-map-taxonomy'][ $key ]=''; | |
| 108 | - } | |
| 109 | - | |
| 110 | - } | |
| 111 | - } | |
| 112 | - | |
| 113 | - | |
| 114 | - } | |
| 115 | - | |
| 116 | - ?> | |
| 117 | - | |
| 118 | - | |
| 119 | - | |
| 120 | - <h3><?php esc_html_e( 'Select the extra fields you want to import', 'mlsimport' ); ?>:</h3> | |
| 121 | - | |
| 122 | - <?php | |
| 123 | - | |
| 124 | - | |
| 125 | - // $options = get_option( 'mlsimport_admin_fields_select', array() ); | |
| 126 | - // print_r($options); | |
| 127 | - | |
| 128 | - | |
| 129 | - | |
| 130 | - // Get the current page and search parameters | |
| 131 | - $current_page = isset( $_GET['mlsimport_page'] ) ? intval( $_GET['mlsimport_page'] ) : 1; | |
| 132 | - $search_term = isset( $_GET['mlsimport_search'] ) ? sanitize_text_field( $_GET['mlsimport_search'] ) : ''; | |
| 133 | - $import_filter = isset( $_GET['mlsimport_filter'] ) ? sanitize_text_field( $_GET['mlsimport_filter'] ) : 'all'; | |
| 134 | - $alpha_filter = isset( $_GET['mlsimport_alpha'] ) ? sanitize_text_field( $_GET['mlsimport_alpha'] ) : ''; | |
| 135 | - | |
| 136 | - // Set up render parameters | |
| 137 | - $render_params = array( | |
| 138 | - 'page' => $current_page, | |
| 139 | - 'fields_per_page' => 99999, // Adjust as needed | |
| 140 | - 'search_term' => $search_term, | |
| 141 | - 'import_filter' => $import_filter, | |
| 142 | - 'alpha_filter' => $alpha_filter, | |
| 143 | - 'show_pagination' => true, | |
| 144 | - 'show_filters' => true, | |
| 145 | - 'show_stats' => true, | |
| 146 | - 'enable_drag_drop' => true, | |
| 147 | - 'form_action' => 'options.php', | |
| 148 | - 'form_method' => 'post', | |
| 149 | - 'form_name' => 'cleanup_options', | |
| 150 | - 'nonce_field' => 'mlsimport_admin_fields_select', | |
| 151 | - 'plugin_name' => 'mlsimport', | |
| 152 | - ); | |
| 153 | - | |
| 154 | - | |
| 155 | - | |
| 156 | - | |
| 157 | - | |
| 158 | - | |
| 159 | - | |
| 160 | - | |
| 161 | - // Then in the admin page, update the function call: | |
| 162 | - echo render_mls_field_selection_interface( | |
| 163 | - $options, | |
| 164 | - $options, | |
| 165 | - $render_params, | |
| 166 | - $theme_schema | |
| 167 | - ); | |
| 168 | - ?> | |
| 169 | - | |
| 170 | - <input type="hidden" name="mlsimport_admin_fields_select[mls-fields-admin][force_rand]" value="<?php echo esc_attr( wp_rand() ); ?>"> | |
| 171 | - | |
| 172 | - | |
| 173 | - <?php mlsimport_add_field_selector_nonce(); ?> | |
| 174 | - </form> | |
| 175 | - <?php | |
| 102 | + echo '<h3>' . esc_html__( 'Select the extra fields you want to import', 'mlsimport' ) . ':</h3>'; | |
| 103 | + echo render_mls_field_selection_interface( | |
| 104 | + $metadata, | |
| 105 | + $configuration, | |
| 106 | + array( | |
| 107 | + 'search_term' => '', | |
| 108 | + 'import_filter' => 'all', | |
| 109 | + 'show_filters' => true, | |
| 110 | + 'show_stats' => true, | |
| 111 | + 'enable_drag_drop' => true, | |
| 112 | + 'plugin_name' => 'mlsimport', | |
| 113 | + ), | |
| 114 | + $theme_schema | |
| 115 | + ); | |
| 176 | 116 | } else { |
| 177 | - // We don't have MLS metadata yet, show waiting message | |
| 178 | - global $mlsimport; | |
| 179 | - $token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient(); | |
| 180 | - if ( trim( $token ) !== '' ) { | |
| 181 | - ?> | |
| 182 | - <div class="mlsimport_warning mlsimport_validated"> | |
| 183 | - <?php | |
| 184 | - esc_html_e( 'We need to gather some information about your MLS. Please Stand By! ', 'mlsimport' ); | |
| 185 | - ?> | |
| 186 | - </div> | |
| 187 | - <?php | |
| 188 | - } else { | |
| 189 | - esc_html_e( 'You are not connected to MLS Import', 'mlsimport' ); | |
| 190 | - } | |
| 117 | + echo '<div class="mlsimport_warning mlsimport_validated">' . esc_html__( 'We need to gather some information about your MLS. Please Stand By! ', 'mlsimport' ) . '</div>'; | |
| 191 | 118 | } |
| 192 | 119 | ?> |
| 193 | -<input type="hidden" id="mlsimport_saas_get_metadata" value="<?php echo esc_attr( wp_create_nonce( "mlsimport_saas_get_metadata" ) ); ?>" /> | |
| 120 | +<input type="hidden" id="mlsimport_field_scope" value="<?php echo esc_attr( (string) $mlsimport_field_scope ); ?>"> | |
| 121 | +<input type="hidden" id="mlsimport_saas_get_metadata" value="<?php echo esc_attr( wp_create_nonce( 'mlsimport_saas_get_metadata' ) ); ?>"> | |