Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 0.12.5

- Added Rent Fetch Sync API key setting with asynchronous validation
- API key field includes a separate "Validate Key" button for testing without saving
- Automatic validation on page load when an API key is present
- Handles multiple validation states: valid, not found, and already in use

## 0.12.4

- Updating hooks to match the new RF pattern for subpages in the General settings.
Expand Down
92 changes: 92 additions & 0 deletions assets/js/rentfetch-rfs-api-key-validation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
jQuery(document).ready(function ($) {
var $apiKeyInput = $('#rentfetch_options_rfs_api_key');
var $validateButton = $('#rfs_validate_api_key');
var $statusDiv = $('#rfs_api_key_status');

/**
* Display a status message
*
* @param {string} message The message to display
* @param {string} type The type of message: 'success', 'error', 'warning', or 'info'
*/
function displayStatus(message, type) {
var cssClass = 'notice notice-' + type;
$statusDiv
.html('<div class="' + cssClass + '"><p>' + message + '</p></div>')
.show();
}

/**
* Validate the API key
*
* @param {boolean} showLoadingState Whether to show a loading state on the button
*/
function validateApiKey(showLoadingState) {
var apiKey = $apiKeyInput.val().trim();

if (!apiKey) {
displayStatus('Please enter an API key.', 'warning');
return;
}

if (showLoadingState) {
$validateButton.prop('disabled', true).text('Validating...');
}

$.post(rfs_ajax_object.ajax_url, {
action: 'rfs_validate_api_key',
api_key: apiKey,
_ajax_nonce: rfs_ajax_object.nonce, // Must match parameter name in PHP: check_ajax_referer('rfs_ajax_nonce', '_ajax_nonce')
})
.done(function (response) {
if (response.success) {
var data = response.data;
var status = data.status;
var message = data.message;

switch (status) {
case 'valid':
displayStatus(message, 'success');
break;
case 'not_found':
displayStatus(message, 'error');
break;
case 'in_use':
displayStatus(message, 'warning');
break;
default:
displayStatus(message, 'info');
break;
}
} else {
displayStatus(
'Error validating API key: ' +
(response.data || 'Unknown error'),
'error'
);
}
})
.fail(function (xhr, status, error) {
displayStatus(
'Network error while validating API key: ' + error,
'error'
);
})
.always(function () {
if (showLoadingState) {
$validateButton.prop('disabled', false).text('Validate Key');
}
});
}

// Validate on button click
$validateButton.on('click', function (e) {
e.preventDefault();
validateApiKey(true);
});

// Validate on page load if API key exists
if ($apiKeyInput.length && $apiKeyInput.val().trim()) {
validateApiKey(false);
}
});
140 changes: 140 additions & 0 deletions lib/admin/ajax-validate-rfs-api-key.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
<?php
/**
* AJAX handler for RFS API key validation
*
* @package rentfetch-sync
*/

if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}

/**
* Validate the RFS API key against the Rent Fetch API
*
* @return void
*/
function rfs_validate_api_key_ajax_handler() {
// Verify nonce
check_ajax_referer( 'rfs_ajax_nonce', '_ajax_nonce' );

// Check if API key is provided
if ( ! isset( $_POST['api_key'] ) ) {
wp_send_json_error( 'API key is required.' );
}

$api_key = sanitize_text_field( wp_unslash( $_POST['api_key'] ) );

if ( empty( $api_key ) ) {
wp_send_json_error( 'API key cannot be empty.' );
}

// Validate the API key against the Rent Fetch API
$validation_result = rfs_validate_api_key_with_api( $api_key );

if ( is_wp_error( $validation_result ) ) {
wp_send_json_error( $validation_result->get_error_message() );
}

wp_send_json_success( $validation_result );
}
add_action( 'wp_ajax_rfs_validate_api_key', 'rfs_validate_api_key_ajax_handler' );

/**
* Validate the API key with the Rent Fetch API
*
* @param string $api_key The API key to validate.
*
* @return array|WP_Error The validation result or WP_Error on failure.
*/
function rfs_validate_api_key_with_api( $api_key ) {

// Get the site URL for validation
$site_url = get_site_url();

// Extract just the domain name (host) from the URL
$parsed_url = wp_parse_url( $site_url );
if ( is_array( $parsed_url ) && isset( $parsed_url['host'] ) ) {
$site_url = $parsed_url['host'];
}

// Prepare the request body
$body = array(
'api_key' => $api_key,
'site_url' => $site_url,
);

// Get the API URL (allow override via constant for testing)
$api_url = defined( 'RENTFETCH_API_VALIDATE_URL' )
? constant( 'RENTFETCH_API_VALIDATE_URL' )
: 'https://api.rentfetch.net/wp-json/rentfetchapi/v1/validate-key';

// Make the API request
$response = wp_remote_post(
$api_url,
array(
'method' => 'POST',
'body' => wp_json_encode( $body ),
'headers' => array(
'Content-Type' => 'application/json',
),
'timeout' => 15,
)
);

// Check for errors
if ( is_wp_error( $response ) ) {
return new WP_Error(
'api_request_failed',
'Unable to connect to Rent Fetch API: ' . $response->get_error_message()
);
}

$response_code = wp_remote_retrieve_response_code( $response );
$response_body = wp_remote_retrieve_body( $response );

// Parse the response
$data = json_decode( $response_body, true );

// Handle different response codes
switch ( $response_code ) {
case 200:
// Valid API key
return array(
'status' => 'valid',
'message' => isset( $data['message'] )
? $data['message']
: 'API key is valid and ready to use.',
);

case 404:
// API key not found
return array(
'status' => 'not_found',
'message' => isset( $data['message'] )
? $data['message']
: 'API key not found. Please check your key and try again.',
);

case 409:
// API key already in use on another site
$existing_site = isset( $data['existing_site'] ) ? $data['existing_site'] : 'another site';
return array(
'status' => 'in_use',
'message' => isset( $data['message'] )
? $data['message']
: sprintf( 'This API key is already in use on %s.', $existing_site ),
);

default:
// Unexpected response
return new WP_Error(
'unexpected_response',
sprintf(
'Unexpected response from API (HTTP %d): %s',
$response_code,
isset( $data['message'] ) ? $data['message'] : wp_remote_retrieve_response_message( $response )
)
);
}
}
29 changes: 29 additions & 0 deletions lib/admin/sync-options.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ function rentfetch_remove_premium_functionality_notice() {
*/
add_action( 'rentfetch_do_settings_general_data_sync', 'rentfetch_settings_sync', 25 );
function rentfetch_settings_sync() {

// Enqueue the API key validation script
wp_enqueue_script( 'rentfetch-rfs-api-key-validation' );

// Localize script to pass AJAX URL and nonce
wp_localize_script(
'rentfetch-rfs-api-key-validation',
'rfs_ajax_object',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'rfs_ajax_nonce' ),
)
);

?>
<div class="header">
<h2 class="title">Data Sync</h2>
Expand Down Expand Up @@ -46,6 +60,21 @@ function rentfetch_settings_sync() {
</ul>
</div>
</div>

<div class="row">
<div class="section">
<label class="label-large" for="rentfetch_options_rfs_api_key">Rent Fetch Sync API Key</label>
<p class="description">Enter your Rent Fetch Sync API key to enable synchronization features.</p>
<div class="white-box">
<div style="margin-bottom: 10px;">
<input type="password" name="rentfetch_options_rfs_api_key" id="rentfetch_options_rfs_api_key" value="<?php echo esc_attr( get_option( 'rentfetch_options_rfs_api_key' ) ); ?>" style="width: 100%; max-width: 400px;">
<button type="button" id="rfs_validate_api_key" class="button" style="margin-left: 10px;">Validate Key</button>
</div>
<div id="rfs_api_key_status"></div>
</div>
</div>
</div>

<style>
.integration-settings { display: none; }
label:has(input[name="rentfetch_options_enabled_integrations[]"]:checked) ~ .integration-settings { display: block; }
Expand Down
8 changes: 8 additions & 0 deletions lib/initialization/enqueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ function rfs_enqueue_backend_scripts() {
RENTFETCHSYNC_VERSION,
true
);

wp_register_script(
'rentfetch-rfs-api-key-validation',
RENTFETCHSYNC_PATH . 'assets/js/rentfetch-rfs-api-key-validation.js',
array( 'jquery' ),
RENTFETCHSYNC_VERSION,
true
);
}
add_action( 'admin_enqueue_scripts', 'rfs_enqueue_backend_scripts' );

Expand Down
4 changes: 2 additions & 2 deletions rentfetch-sync.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Plugin Name: Rent Fetch Sync
Plugin URI: https://github.com/jonschr/rentfetch-sync
Description: An addon for Rent Fetch that syncs properties, floorplans, and units
Version: 0.12.4
Version: 0.12.5
Author: Brindle Digital
Author URI: https://www.brindledigital.com/

Expand All @@ -24,7 +24,7 @@
}

// Define the version of the plugin.
define( 'RENTFETCHSYNC_VERSION', '0.12.4' );
define( 'RENTFETCHSYNC_VERSION', '0.12.5' );

// Plugin directory.
define( 'RENTFETCHSYNC_DIR', plugin_dir_path( __FILE__ ) );
Expand Down