PluginProbe
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings / 7.1.1
MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings v7.1.1
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 6.0.5 All 36 releases
mlsimport / includes / mlsimport-field-selector-functions.php

mlsimport-field-selector-functions.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.1.1, at includes/mlsimport-field-selector-functions.php

243 lines 12.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Render the form-free Field Configuration interface.
4 *
5 * PHP renders the current active metadata in authoritative custom order. The
6 * browser may search, filter, sort, bulk-select visible rows, and enqueue compact
7 * commands, but it does not submit a mirrored WordPress settings form. Dormant
8 * fields remain in storage through the domain module and are intentionally not
9 * rendered until their MLS metadata returns.
10 *
11 * @package MLSImport
12 * @subpackage MLSImport/includes
13 */
14
15 if ( ! defined( 'ABSPATH' ) ) {
16 exit;
17 }
18
19 /**
20 * Order current metadata by the saved custom sequence.
21 *
22 * Only keys present in current metadata are returned. Any metadata field absent
23 * from the normalized order is appended alphabetically as a safe read-only
24 * fallback; metadata reconciliation normally added it before this render.
25 *
26 * @param array $fields Current MLS metadata keyed by field name.
27 * @param array $options Normalized Field Configuration.
28 * @param array $params Render parameters retained for the public signature.
29 * @return array Ordered active metadata.
30 */
31 function prepare_mls_fields_for_display( $fields, $options, $params = array() ) {
32 $fields = is_array( $fields ) ? $fields : array();
33 $options = is_array( $options ) ? $options : array();
34 $order = isset( $options['field_order'] ) && is_array( $options['field_order'] ) ? $options['field_order'] : array();
35 asort( $order, SORT_NUMERIC );
36
37 $ordered = array();
38 foreach ( array_keys( $order ) as $field_key ) {
39 if ( array_key_exists( $field_key, $fields ) ) {
40 $ordered[ $field_key ] = $fields[ $field_key ];
41 }
42 }
43
44 $remaining = array_diff_key( $fields, $ordered );
45 ksort( $remaining, SORT_STRING );
46
47 return array_merge( $ordered, $remaining );
48 }
49
50 /**
51 * Render the complete active Field Configuration list.
52 *
53 * @param array $fields Current MLS metadata.
54 * @param array $options Normalized Field Configuration.
55 * @param array $render_params Initial filter and feature controls.
56 * @param array $theme_schema Theme defaults retained for compatible callers.
57 * @return string Complete interface HTML.
58 */
59 function render_mls_field_selection_interface( $fields, $options, $render_params = array(), $theme_schema = array() ) {
60 $params = wp_parse_args(
61 $render_params,
62 array(
63 'import_filter' => 'all',
64 'show_filters' => true,
65 'show_stats' => true,
66 'enable_drag_drop' => true,
67 'plugin_name' => 'mlsimport',
68 )
69 );
70 $display_fields = prepare_mls_fields_for_display( $fields, $options, $params );
71 $revision = max( 0, (int) ( $options[ Mlsimport_Field_Configuration::REVISION_KEY ] ?? 0 ) );
72 $taxonomies = mlsimport_field_configuration_taxonomies();
73
74 ob_start();
75 echo '<div class="mlsimport-field-selector-container" data-revision="' . esc_attr( $revision ) . '" data-initial-filter="' . esc_attr( $params['import_filter'] ) . '">';
76 echo '<div class="mlsimport-field-save-status" role="status" aria-live="polite"></div>';
77
78 if ( $params['show_stats'] ) {
79 mlsimport_render_field_stats( $display_fields, $options );
80 }
81 if ( $params['show_filters'] ) {
82 mlsimport_render_field_filters( $params );
83 }
84
85 echo '<table class="mlsimport-fields-table" id="mlsimport-fields-table">';
86 mlsimport_render_field_table_header();
87 echo '<tbody id="mlsimport-fields-table-body">';
88 foreach ( $display_fields as $field_key => $description ) {
89 mlsimport_render_field_table_row( $field_key, $description, $options, $taxonomies );
90 }
91 if ( empty( $display_fields ) ) {
92 echo '<tr class="mlsimport-no-fields"><td colspan="7">' . esc_html__( 'No MLS fields are available.', 'mlsimport' ) . '</td></tr>';
93 }
94 echo '</tbody></table></div>';
95
96 return ob_get_clean();
97 }
98
99 /**
100 * Render active-field statistics and visible-only bulk controls.
101 *
102 * Counts cover only current MLS metadata because dormant fields are retained in
103 * storage but absent from this interface. Bulk buttons describe their visible
104 * scope explicitly; the controller sends the exact visible field keys.
105 *
106 * @param array $fields Ordered active metadata.
107 * @param array $options Normalized Field Configuration.
108 * @return void
109 */
110 function mlsimport_render_field_stats( array $fields, array $options ) {
111 $selected = 0;
112 foreach ( array_keys( $fields ) as $field_key ) {
113 $selected += ! empty( $options['mls-fields'][ $field_key ] ) ? 1 : 0;
114 }
115
116 echo '<div class="mlsimport-field-stats"><ul>';
117 echo '<li class="mlsimport-total-count">' . esc_html( sprintf( __( '%d fields total', 'mlsimport' ), count( $fields ) ) ) . '</li>';
118 echo '<li class="mlsimport-selected-count">' . esc_html( sprintf( __( '%d marked for import', 'mlsimport' ), $selected ) ) . '</li>';
119 echo '</ul><div class="mlsimport-button-action-wrapper">';
120 echo '<button type="button" id="mlsimport-select-all-import" class="button mlsimport_button">' . esc_html__( 'Import - Select all visible', 'mlsimport' ) . '</button>';
121 echo '<button type="button" id="mlsimport-select-none-import" class="button mlsimport_button">' . esc_html__( 'Import - Select none visible', 'mlsimport' ) . '</button>';
122 echo '<button type="button" id="mlsimport-select-all-admin" class="button mlsimport_button secondary">' . esc_html__( 'Hidden from Public - Select all visible', 'mlsimport' ) . '</button>';
123 echo '<button type="button" id="mlsimport-select-none-admin" class="button mlsimport_button secondary">' . esc_html__( 'Hidden from Public - Select none visible', 'mlsimport' ) . '</button>';
124 echo '</div></div>';
125 }
126
127 /**
128 * Render client-side search, import filter, and sort controls.
129 *
130 * These controls change only the local view. Custom order remains the sole view
131 * where reordering is enabled, avoiding position commands derived from a
132 * filtered or alphabetically sorted subset.
133 *
134 * @param array $params Parsed renderer parameters including initial filter.
135 * @return void
136 */
137 function mlsimport_render_field_filters( array $params ) {
138 echo '<div class="mlsimport-field-filters">';
139 echo '<select id="mlsimport-field-sort" class="mlsimport-field-filter mlsimport-2025-select" style="width:200px">';
140 echo '<option value="custom_order">' . esc_html__( 'Custom Order', 'mlsimport' ) . '</option>';
141 echo '<option value="field_asc">' . esc_html__( 'Field Name (A-Z)', 'mlsimport' ) . '</option>';
142 echo '<option value="field_desc">' . esc_html__( 'Field Name (Z-A)', 'mlsimport' ) . '</option>';
143 echo '<option value="label_asc">' . esc_html__( 'Label (A-Z)', 'mlsimport' ) . '</option>';
144 echo '<option value="label_desc">' . esc_html__( 'Label (Z-A)', 'mlsimport' ) . '</option>';
145 echo '<option value="postmeta_asc">' . esc_html__( 'Property Detail Field (A-Z)', 'mlsimport' ) . '</option>';
146 echo '<option value="postmeta_desc">' . esc_html__( 'Property Detail Field (Z-A)', 'mlsimport' ) . '</option>';
147 echo '<option value="category_asc">' . esc_html__( 'Category (A-Z)', 'mlsimport' ) . '</option>';
148 echo '<option value="category_desc">' . esc_html__( 'Category (Z-A)', 'mlsimport' ) . '</option>';
149 echo '</select>';
150
151 echo '<select id="mlsimport-import-filter" class="mlsimport-field-filter mlsimport-2025-select" style="width:200px">';
152 echo '<option value="all"' . selected( $params['import_filter'], 'all', false ) . '>' . esc_html__( 'All Fields', 'mlsimport' ) . '</option>';
153 echo '<option value="selected"' . selected( $params['import_filter'], 'selected', false ) . '>' . esc_html__( 'Selected Only', 'mlsimport' ) . '</option>';
154 echo '<option value="not_selected"' . selected( $params['import_filter'], 'not_selected', false ) . '>' . esc_html__( 'Not Selected', 'mlsimport' ) . '</option>';
155 echo '</select>';
156 echo '<input type="text" id="mlsimport-field-search" class="mlsimport-field-filter mlsimport-input mlsimport-2025-input" style="width:300px" placeholder="' . esc_attr__( 'Search...', 'mlsimport' ) . '">';
157 echo '</div>';
158 }
159
160 /**
161 * Render the seven stable table columns consumed by the browser controller.
162 *
163 * No form names are emitted because changes travel as compact JSON commands.
164 * The actions column holds relative move controls that remain usable when the
165 * jQuery sortable interaction is unavailable.
166 *
167 * @return void
168 */
169 function mlsimport_render_field_table_header() {
170 echo '<thead><tr>';
171 echo '<th>' . esc_html__( 'Field', 'mlsimport' ) . '</th>';
172 echo '<th style="width:75px">' . esc_html__( 'Import it?', 'mlsimport' ) . '</th>';
173 echo '<th>' . esc_html__( 'Front End Label', 'mlsimport' ) . '</th>';
174 echo '<th style="width:75px">' . esc_html__( 'Hidden from Public?', 'mlsimport' ) . '</th>';
175 echo '<th>' . esc_html__( 'Property Detail Field (post meta)', 'mlsimport' ) . '</th>';
176 echo '<th>' . esc_html__( 'Category', 'mlsimport' ) . '</th>';
177 echo '<th style="width:140px">' . esc_html__( 'Actions', 'mlsimport' ) . '</th>';
178 echo '</tr></thead>';
179 }
180
181 /**
182 * Render one active field without form names or mandatory-field branches.
183 *
184 * Values come exclusively from the normalized configuration. The row exposes
185 * the RESO key and custom-order position as data attributes, while editable
186 * controls expose only classes interpreted by the compact command controller.
187 *
188 * @param string $field_key RESO field identifier.
189 * @param mixed $description Metadata description displayed below the key.
190 * @param array $options Normalized Field Configuration.
191 * @param array $taxonomies Allowed taxonomy destinations.
192 * @return void
193 */
194 function mlsimport_render_field_table_row( $field_key, $description, array $options, array $taxonomies ) {
195 $order = (int) ( $options['field_order'][ $field_key ] ?? 0 );
196 $is_import = ! empty( $options['mls-fields'][ $field_key ] );
197 $is_admin = ! empty( $options['mls-fields-admin'][ $field_key ] );
198 $label = (string) ( $options['mls-fields-label'][ $field_key ] ?? '' );
199 $postmeta = (string) ( $options['mls-fields-map-postmeta'][ $field_key ] ?? '' );
200 $taxonomy = (string) ( $options['mls-fields-map-taxonomy'][ $field_key ] ?? '' );
201 $description = is_scalar( $description ) && ! is_bool( $description ) ? (string) $description : '';
202
203 echo '<tr class="mlsimport-field-row" data-field-order="' . esc_attr( $order ) . '" data-field-key="' . esc_attr( $field_key ) . '">';
204 echo '<td class="mlsimport-field-name mlsimport-field-name_title_desc"><span class="field-name"><span class="field-position">' . esc_html( $order + 1 ) . '. </span>' . esc_html( $field_key ) . '</span>';
205 if ( '' !== $description && '1' !== $description && '0' !== $description ) {
206 echo '<div class="field-explanation">' . esc_html( $description ) . '</div>';
207 }
208 echo '</td>';
209 echo '<td class="mlsimport-field-import"><input type="checkbox" class="mlsimport-import-checkbox" value="1" ' . checked( $is_import, true, false ) . '></td>';
210 echo '<td class="mlsimport-field-label"><input type="text" class="mlsimport-label-input mlsimport-input mlsimport-2025-input" value="' . esc_attr( $label ) . '" placeholder="' . esc_attr__( 'enter label...', 'mlsimport' ) . '"></td>';
211 echo '<td class="mlsimport-field-admin"><input type="checkbox" class="mlsimport-admin-checkbox" value="1" ' . checked( $is_admin, true, false ) . '></td>';
212 echo '<td class="mlsimport-field-postmeta"><input type="text" class="mlsimport-postmeta-input mlsimport-input mlsimport-2025-input" value="' . esc_attr( $postmeta ) . '"></td>';
213 echo '<td class="mlsimport-field-taxonomy">' . mlsimport_field_taxonomy_dropdown( $taxonomy, $taxonomies ) . '</td>';
214 echo '<td class="mlsimport-field-actions"><div class="mlsimport-row-actions">';
215 echo '<button type="button" class="mlsimport-move-btn mlsimport-move-up" title="' . esc_attr__( 'Move field up', 'mlsimport' ) . '">↑</button>';
216 echo '<button type="button" class="mlsimport-move-btn mlsimport-move-down" title="' . esc_attr__( 'Move field down', 'mlsimport' ) . '">↓</button>';
217 echo '<button type="button" class="mlsimport-move-btn mlsimport-move-top" title="' . esc_attr__( 'Move field to top', 'mlsimport' ) . '">⇈</button>';
218 echo '<button type="button" class="mlsimport-move-btn mlsimport-move-bottom" title="' . esc_attr__( 'Move field to bottom', 'mlsimport' ) . '">⇊</button>';
219 echo '</div></td></tr>';
220 }
221
222 /**
223 * Build the active property type's allowed taxonomy destination control.
224 *
225 * An explicit empty option represents no taxonomy mapping. The selected value
226 * is read from normalized state, and every value and label is escaped before
227 * the complete select element is returned to the row renderer.
228 *
229 * @param string $selected_taxonomy Currently mapped taxonomy slug.
230 * @param array $taxonomies Allowed slug-to-label map.
231 * @return string Escaped select markup.
232 */
233 function mlsimport_field_taxonomy_dropdown( $selected_taxonomy, array $taxonomies ) {
234 $html = '<select class="mlsimport-taxonomy-select mlsimport-2025-select">';
235 $html .= '<option value="">' . esc_html__( 'None', 'mlsimport' ) . '</option>';
236 foreach ( $taxonomies as $taxonomy => $label ) {
237 $html .= '<option value="' . esc_attr( $taxonomy ) . '" ' . selected( $selected_taxonomy, $taxonomy, false ) . '>' . esc_html( $label ) . '</option>';
238 }
239 $html .= '</select>';
240
241 return $html;
242 }
243