PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.2.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.2.1
7.2.2 7.2.1 7.2 7.1.2 7.1.1 7.1 7.0.4 7.0.6 7.0.7 6.3.8 6.3.7 6.3.6 6.3.5 6.3.4 6.3.3 6.3.1 trunk 5.7.3 5.7.5 5.8.1 5.8.2 5.8.3 5.8.4 5.8.6 6.0.4 All 37 releases
← All changes | admin/partials/mlsimport-admin-fields-select.php +98 -215 7.0.67.2.1 View file →
@@ -1,238 +1,121 @@
1 -<?php
1 +<?php
2 2 /**
3 - * Admin partial: "Select Import fields" tab (Field Options).
3 + * Read-only renderer for the Field Configuration settings tab.
4 4 *
5 - * Renders the RESO field-selection interface for the active MLS. It first gates
6 - * on connectivity (a valid SaaS token AND a successful MLS connection), then — if
7 - * the MLS metadata has been populated — builds the per-field options structure
8 - * (defaults derived from the theme schema) and hands it to
9 - * render_mls_field_selection_interface(). Until metadata arrives it shows a
10 - * "please stand by" notice. The progressive-save JS persists edits field by field.
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.
11 10 *
12 - * @package mlsimport
13 - * @subpackage mlsimport/admin/partials
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
14 31 */
15 32
16 -// Block direct access outside of WordPress.
17 33 if ( ! defined( 'ABSPATH' ) ) {
18 - exit; // Exit if accessed directly
34 + exit;
19 35 }
20 -// Theme-specific RESO->post-meta schema (defines which fields map where by default).
21 -$theme_schema= mlsimport_hardocde_theme_schema();
22 36
23 -// Primary plugin options and the metadata-populated flag for this MLS.
24 -$options = get_option( 'mlsimport_admin_options' );
25 -$mlsimport_mls_metadata_populated = get_option( 'mlsimport_mls_metadata_populated', '' );
26 -// Allowed HTML tag set for any rendered content.
27 -$permited_tags = mlsimport_allowed_html_tags_content();
28 -// Resolve the active theme's property post type (if the adapter exposes it).
29 -$post_type = '';
30 -if (method_exists($this->env_data, 'get_property_post_type')) {
31 - $post_type = $this->env_data->get_property_post_type();
32 -}
37 +global $mlsimport;
33 38
34 -// Taxonomies registered against that post type — the taxonomy-mapping dropdowns use these.
35 -$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();
36 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();
37 47
38 -// SaaS/MLS connection gate: fetch the cached API token and prior connection result.
39 -global $mlsimport;
40 -$token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient();
41 -$is_mls_connected = get_option('mlsimport_connection_test', '');
48 +// Step 2: no SaaS account token — nothing on this tab can work.
42 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 +}
43 55
44 -// Not yet confirmed connected — re-test the MLS connection now and re-read the flag.
45 -if ('yes' !== $is_mls_connected) {
46 - $mlsimport->admin->mlsimport_saas_check_mls_connection();
47 - $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'] ?? '' ) : '';
48 68 }
49 69
50 -// No SaaS token — the account credentials are missing/invalid; stop here.
51 -if (trim($token) === '') {
52 - echo '<div class="mlsimport_warning">' . esc_html__('You are not connected to MlsImport - Please check your Username and Password.', 'mlsimport') . '</div>';
53 - 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;
54 73 }
55 74
56 -// Have a token but the MLS itself did not connect — stop here.
57 -if ('yes' !== $is_mls_connected) {
58 - 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>';
59 - 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>';
60 94 }
61 95
62 -// Metadata is present — render the full field-selection form.
63 -if ( 'yes' === $mlsimport_mls_metadata_populated ) {
64 - // We have MLS metadata, so we can show the field selection interface
65 - ?>
66 - <form method="post" class= "mlsimport-import-fields-form" name="cleanup_options" action="options.php">
67 - <?php
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() );
68 101
69 -
70 - // Add this to the beginning of the form processing
71 - // Current saved field-selection options (empty on first visit).
72 - $options = get_option('mlsimport_admin_fields_select', array());
73 -
74 -
75 - // Ensure all arrays are initialized
76 - // First run (no saved options) — seed the structure from the MLS metadata.
77 - if (!is_array($options) ||
78 - (is_array($options) && empty($options)) ) {
79 -
80 - // Raw MLS field metadata (JSON) captured during connection.
81 - $mlsimport_mls_metadata_mls_data = get_option( 'mlsimport_mls_metadata_mls_data', '' );
82 - $metadata_api_call_data_service_property = json_decode( $mlsimport_mls_metadata_mls_data, true );
83 - $options = array();
84 -
85 -
86 -
87 - // Whether each field is imported (0/1).
88 - if (!isset($options['mls-fields'])) {
89 - $options['mls-fields'] = array();
90 - }
91 -
92 - // Whether each field is admin-only (0/1).
93 - if (!isset($options['mls-fields-admin'])) {
94 - $options['mls-fields-admin'] = array();
95 - }
96 -
97 - // Per-field display label override.
98 - if (!isset($options['mls-fields-label'])) {
99 - $options['mls-fields-label'] = array();
100 - }
101 -
102 - // Per-field target post-meta key.
103 - if (!isset($options['mls-fields-map-postmeta'])) {
104 - $options['mls-fields-map-postmeta'] = array();
105 - }
106 -
107 - // Per-field target taxonomy.
108 - if (!isset($options['mls-fields-map-taxonomy'])) {
109 - $options['mls-fields-map-taxonomy'] = array();
110 - }
111 -
112 -
113 - // Running index used to record each field's display order.
114 - $order_item=0;
115 - // Guard against non-array metadata (bad/empty JSON).
116 - if (!is_array($metadata_api_call_data_service_property)) {
117 - $metadata_api_call_data_service_property = [];
118 - }
119 -
120 - // Alphabetise fields by key for a stable initial order.
121 - ksort($metadata_api_call_data_service_property);
122 -
123 - // Seed defaults for every MLS field.
124 - foreach ( $metadata_api_call_data_service_property as $key => $value ) {
125 - $description = 'no description ';
126 -
127 - // Default: not imported, next in order, not admin-only, no meta/label mapping.
128 - $options['mls-fields'][ $key ]=0;
129 - $options['field_order'][ $key ]=$order_item++;
130 - $options['mls-fields-admin'][ $key ]=0 ;
131 - $options['mls-fields-map-postmeta'][ $key ]='';
132 -
133 - $options['mls-fields-label'][ $key ]='';
134 -
135 - // Field is part of the theme schema — turn it on by default.
136 - if ( array_key_exists( $key, $theme_schema ) ) {
137 - $options['mls-fields'][ $key ]=1;
138 -
139 - // Schema marks it as a taxonomy — pre-map it to that taxonomy name.
140 - if( isset( $theme_schema[$key]['type']) && $theme_schema[$key]['type']=='taxonomy' ){
141 - $options['mls-fields-map-taxonomy'][ $key ]=$theme_schema[$key]['name'];
142 - }else{
143 - // Otherwise leave the taxonomy mapping empty (meta field).
144 - $options['mls-fields-map-taxonomy'][ $key ]='';
145 - }
146 -
147 - }
148 - }
149 -
150 -
151 - }
152 -
153 - ?>
154 -
155 -
156 -
157 - <h3><?php esc_html_e( 'Select the extra fields you want to import', 'mlsimport' ); ?>:</h3>
158 -
159 - <?php
160 -
161 -
162 - // $options = get_option( 'mlsimport_admin_fields_select', array() );
163 - // print_r($options);
164 -
165 -
166 -
167 - // Get the current page and search parameters
168 - // Pagination + filter/search state, all read from the query string.
169 - $current_page = isset( $_GET['mlsimport_page'] ) ? intval( $_GET['mlsimport_page'] ) : 1;
170 - $search_term = isset( $_GET['mlsimport_search'] ) ? sanitize_text_field( $_GET['mlsimport_search'] ) : '';
171 - $import_filter = isset( $_GET['mlsimport_filter'] ) ? sanitize_text_field( $_GET['mlsimport_filter'] ) : 'all';
172 - $alpha_filter = isset( $_GET['mlsimport_alpha'] ) ? sanitize_text_field( $_GET['mlsimport_alpha'] ) : '';
173 -
174 - // Set up render parameters
175 - // Config passed to the field-selection renderer (paging, filters, form, UI toggles).
176 - $render_params = array(
177 - 'page' => $current_page,
178 - 'fields_per_page' => 99999, // Adjust as needed
179 - 'search_term' => $search_term,
180 - 'import_filter' => $import_filter,
181 - 'alpha_filter' => $alpha_filter,
182 - 'show_pagination' => true,
183 - 'show_filters' => true,
184 - 'show_stats' => true,
185 - 'enable_drag_drop' => true,
186 - 'form_action' => 'options.php',
187 - 'form_method' => 'post',
188 - 'form_name' => 'cleanup_options',
189 - 'nonce_field' => 'mlsimport_admin_fields_select',
190 - 'plugin_name' => 'mlsimport',
191 - );
192 -
193 -
194 -
195 -
196 -
197 -
198 -
199 -
200 - // Then in the admin page, update the function call:
201 - // Render the full drag-and-drop field-selection table.
202 - echo render_mls_field_selection_interface(
203 - $options,
204 - $options,
205 - $render_params,
206 - $theme_schema
207 - );
208 - ?>
209 -
210 - <input type="hidden" name="mlsimport_admin_fields_select[mls-fields-admin][force_rand]" value="<?php echo esc_attr( wp_rand() ); ?>">
211 -
212 -
213 - <?php
214 - // Output the security nonce that the field-selector save handler checks.
215 - mlsimport_add_field_selector_nonce(); ?>
216 - </form>
217 - <?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 + );
218 116 } else {
219 - // We don't have MLS metadata yet, show waiting message
220 - // Connected but metadata still being gathered — show a stand-by notice.
221 - global $mlsimport;
222 - $token = $mlsimport->admin->mlsimport_saas_get_mls_api_token_from_transient();
223 - // Token present — connected, just waiting on metadata.
224 - if ( trim( $token ) !== '' ) {
225 - ?>
226 - <div class="mlsimport_warning mlsimport_validated">
227 - <?php
228 - esc_html_e( 'We need to gather some information about your MLS. Please Stand By! ', 'mlsimport' );
229 - ?>
230 - </div>
231 - <?php
232 - } else {
233 - // No token — not connected to the SaaS at all.
234 - esc_html_e( 'You are not connected to MLS Import', 'mlsimport' );
235 - }
117 + echo '<div class="mlsimport_warning mlsimport_validated">' . esc_html__( 'We need to gather some information about your MLS. Please Stand By! ', 'mlsimport' ) . '</div>';
236 118 }
237 119 ?>
238 -<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' ) ); ?>">