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.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-connections-remove.php

mlsimport-connections-remove.php in MLSImport: IDX Plugin & MLS Plugin for Real Estate Listings 7.2.1, at includes/mlsimport-connections-remove.php

170 lines 6.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Remove-connection flow — server side.
4 *
5 * WHY THIS FILE EXISTS
6 * --------------------
7 * The Connections tab lets an administrator remove an MLS connection (a row's
8 * Remove action, admin/js/mlsimport-connections.js). This file owns everything
9 * remove needs server-side, mirroring the add/edit module layout
10 * (includes/mlsimport-connections-add.php / -edit.php):
11 *
12 * - the flow core: delete the connection's per-connection suffixed options,
13 * drop its registry record, and — when the removed connection was the
14 * CURRENT one — promote the next-highest-priority remaining connection to
15 * current (mirroring its identity + credentials into the flat
16 * mlsimport_admin_options, which every legacy read path resolves current
17 * from). Removing the LAST connection leaves the install unconfigured,
18 * - the single AJAX handler the row's Remove action posts to.
19 *
20 * ONE RULE (what remove means): removing a connection deletes the connection
21 * itself — record, credentials, field mapping, gathered metadata. It never
22 * deletes content: imported listings and import tasks stay (tasks bound to the
23 * removed connection simply stop running — task binding fails closed, #277).
24 *
25 * @since 7.3.0
26 * @package Mlsimport
27 */
28
29 if ( ! defined( 'ABSPATH' ) ) {
30 exit;
31 }
32
33 /**
34 * Run the full remove flow: refuse / clean up / drop / promote.
35 *
36 * Step by step:
37 * 1. Refuse: no id, or not a registered connection (the same "must name a
38 * registered connection" rule the edit flow uses).
39 * 2. Note whether the removed connection is the CURRENT one — decided BEFORE
40 * anything is deleted.
41 * 3. Delete the connection's suffixed per-connection options (the same five
42 * bases the #274 migration creates: field selection, sync settings, the
43 * two metadata blobs, the metadata-populated flag).
44 * 4. Drop the registry record.
45 * 5. Current-connection promotion: when the removed connection was current,
46 * the highest-priority REMAINING connection becomes current — its identity
47 * + credentials are mirrored into the flat mlsimport_admin_options through
48 * the shared mlsimport_connections_flat_sync(), the provider access tokens
49 * are dropped so the next MLS request re-authenticates as the promoted
50 * connection, and the global connection flag follows the promoted record's
51 * tested status. With NO connection remaining the flat identity is cleared
52 * and the flag deleted — the install is unconfigured again.
53 * 6. Priority drives the dedupe winner (#267/#282): removal is a removal seam,
54 * so every currently flagged duplicate group is re-decided — a hidden
55 * duplicate whose winner belonged to the removed connection is promoted.
56 *
57 * @param int $mls_id The connection being removed.
58 * @return array{success:bool, code:string, message:string}
59 */
60 function mlsimport_connections_remove_execute( int $mls_id ): array {
61 // Step 1: cheap refusals first — same pure rule as the edit flow.
62 $refusal_messages = array(
63 'no_mls' => esc_html__( 'No connection selected.', 'mlsimport' ),
64 'not_registered' => esc_html__( 'This MLS is not one of your connections.', 'mlsimport' ),
65 );
66 $refusal = mlsimport_connections_edit_refusal( $mls_id, array_keys( Mlsimport_Connections::all() ) );
67 if ( null !== $refusal ) {
68 return array(
69 'success' => false,
70 'code' => $refusal,
71 'message' => $refusal_messages[ $refusal ],
72 );
73 }
74
75 // Step 2: current-ness is decided before any state changes.
76 $was_current = ( $mls_id === mlsimport_current_mls_id() );
77
78 // Step 3: the connection's large per-MLS state goes with it.
79 foreach ( array(
80 'mlsimport_admin_fields_select',
81 'mlsimport_admin_mls_sync',
82 'mlsimport_mls_metadata_mls_data',
83 'mlsimport_mls_metadata_mls_enums',
84 'mlsimport_mls_metadata_populated',
85 ) as $base ) {
86 mlsimport_delete_connection_option( $base, $mls_id );
87 }
88
89 // Step 4: the record itself.
90 Mlsimport_Connections::remove( $mls_id );
91
92 // Step 5: promotion when the current connection was removed.
93 if ( $was_current ) {
94 $options = get_option( 'mlsimport_admin_options', array() );
95 $options = is_array( $options ) ? $options : array();
96 $remaining = Mlsimport_Connections::all();
97
98 if ( array() !== $remaining ) {
99 // The registry is priority-sorted: the first record is the new
100 // current. Mirror identity + credentials into the flat options.
101 $promoted = reset( $remaining );
102 $adapter = Mlsimport_Provider_Family::adapter( (string) $promoted['provider_type'], (string) $promoted['mls_id'] );
103 update_option(
104 'mlsimport_admin_options',
105 mlsimport_connections_flat_sync(
106 $options,
107 $promoted,
108 mlsimport_connections_credential_options( $promoted, $adapter->credential_fields() )
109 )
110 );
111 // The flag follows the promoted record's own tested status — a
112 // promotion never fabricates a passed test.
113 update_option( 'mlsimport_connection_test', 'yes' === $promoted['status'] ? 'yes' : '' );
114 } else {
115 // Last connection removed: unconfigured install. Identity cleared;
116 // other flat keys (account login, theme choice, stored provider
117 // credential slots) stay untouched by design.
118 $options['mlsimport_mls_name'] = '';
119 $options['mlsimport_mls_name_front'] = '';
120 update_option( 'mlsimport_admin_options', $options );
121 delete_option( 'mlsimport_connection_test' );
122 }
123
124 // Either way the old current connection's tokens must not be reused.
125 Mlsimport_Provider_Family::clear_direct_access_tokens();
126 }
127
128 // Step 6: re-decide flagged duplicates under the shrunk connection set.
129 mlsimport_dedupe_reevaluate_flagged();
130
131 return array(
132 'success' => true,
133 'code' => '',
134 'message' => '',
135 );
136 }
137
138 /**
139 * AJAX: a row's Remove action.
140 *
141 * Step by step:
142 * 1. Nonce (the Connections screen nonce) + administrator capability.
143 * 2. Run the flow core for the posted mls_id and answer the JS, which
144 * reloads the page on success (table, slot strip, and current-connection
145 * surfaces all re-render server-side).
146 *
147 * @return void
148 */
149 function mlsimport_ajax_connections_remove() {
150 // Step 1: security.
151 check_ajax_referer( 'mlsimport_connections_screen', 'security' );
152 if ( ! current_user_can( 'administrator' ) ) {
153 wp_send_json_error( array( 'message' => 'Unauthorized' ) );
154 }
155
156 // Step 2: run the flow and answer the row.
157 $outcome = mlsimport_connections_remove_execute( isset( $_POST['mls_id'] ) ? (int) $_POST['mls_id'] : 0 );
158 if ( ! $outcome['success'] ) {
159 wp_send_json_error(
160 array(
161 'code' => $outcome['code'],
162 'message' => $outcome['message'],
163 )
164 );
165 }
166 wp_send_json_success();
167 }
168
169 add_action( 'wp_ajax_mlsimport_connections_remove', 'mlsimport_ajax_connections_remove' );
170