--- a/rentfetch/lib/admin/ajax-handlers/warm-cache.php
+++ b/rentfetch/lib/admin/ajax-handlers/warm-cache.php
@@ -95,14 +95,28 @@
$formatted = array();
foreach ( $popular as $search_key => $search_data ) {
- $query_string = http_build_query( $search_data['params'] );
+ $sanitized_params = rentfetch_sanitize_search_params( $search_data['params'] );
+ $query_string = http_build_query( $sanitized_params );
+ $display_params = $sanitized_params;
+
+ if ( empty( $display_params ) || ( 1 === count( $display_params ) && isset( $display_params['availability'] ) && '1' === (string) $display_params['availability'] ) ) {
+ $display_query = '(all available)';
+ } else {
+ unset( $display_params['availability'] );
+ $display_query = urldecode( http_build_query( $display_params ) );
+ if ( '' === $display_query ) {
+ $display_query = '(all available)';
+ }
+ }
+
$percentage = $total_searches > 0 ? round( ( $search_data['count'] / $total_searches ) * 100, 1 ) : 0;
$formatted[] = array(
- 'type' => $search_data['type'],
- 'query' => empty( $query_string ) ? '(no filters)' : $query_string,
- 'count' => $search_data['count'],
- 'percentage' => $percentage,
- 'last_used' => human_time_diff( $search_data['last_used'] ) . ' ago',
+ 'type' => esc_html( sanitize_text_field( $search_data['type'] ) ),
+ 'query' => esc_html( $query_string ),
+ 'display_query' => esc_html( $display_query ),
+ 'count' => esc_html( (string) absint( $search_data['count'] ) ),
+ 'percentage' => esc_html( (string) $percentage ),
+ 'last_used' => esc_html( human_time_diff( absint( $search_data['last_used'] ) ) . ' ago' ),
);
}
--- a/rentfetch/lib/admin/options-pages-setup/main-options-page-wrapper-markup.php
+++ b/rentfetch/lib/admin/options-pages-setup/main-options-page-wrapper-markup.php
@@ -37,7 +37,7 @@
echo '<nav class="nav-tab-wrapper">';
$active = ( 'general' === $tab ) ? 'nav-tab-active' : '';
- printf( '<a href="%s" class="nav-tab %s">%s</a>', esc_url( admin_url( 'admin.php?page=rentfetch-options' ) ), esc_html( $active ), esc_html( 'General' ) );
+ printf( '<a href="%s" class="nav-tab %s">%s</a>', esc_url( admin_url( 'admin.php?page=rentfetch-options&tab=general§ion=data-sync' ) ), esc_html( $active ), esc_html( 'General' ) );
$active = ( 'floorplans' === $tab ) ? 'nav-tab-active' : '';
printf( '<a href="%s" class="nav-tab %s">%s</a>', esc_url( admin_url( 'admin.php?page=rentfetch-options&tab=floorplans' ) ), esc_html( $active ), esc_html( 'Floor Plan Settings' ) );
--- a/rentfetch/lib/admin/options-sections/options-general-section.php
+++ b/rentfetch/lib/admin/options-sections/options-general-section.php
@@ -20,63 +20,100 @@
add_option( 'rentfetch_options_enable_search_indexes', '1' );
add_option( 'rentfetch_options_enable_cache_warming', '0' );
add_option( 'rentfetch_options_enable_search_tracking', '1' );
+ add_option( 'rentfetch_options_enable_analytics', '1' );
+ add_option( 'rentfetch_options_enable_analytics_debug', '0' );
}
register_activation_hook( RENTFETCH_BASENAME, 'rentfetch_settings_set_defaults_general' );
/**
- * Adds the general settings section to the Rent Fetch settings page.
+ * Get the general settings section from the query string.
+ *
+ * @return string
*/
-function rentfetch_settings_general() {
+function rentfetch_settings_get_general_section() {
+ $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : '';
+
+ if ( '' === $section ) {
+ return 'data-sync';
+ }
- // Silence is golden.
+ return $section;
}
-add_action( 'rentfetch_do_settings_general', 'rentfetch_settings_general' );
/**
- * Add the notice about the sync functionality (this will be removed by the sync plugin if it's installed)
- *
- * @return void
+ * Adds the general settings section to the Rent Fetch settings page.
*/
-function rentfetch_settings_sync_functionality_notice() {
+function rentfetch_settings_general() {
+ $section = rentfetch_settings_get_general_section();
+
echo '<section id="rent-fetch-general-page" class="options-container">';
-
echo '<div class="rent-fetch-options-nav-wrap">';
echo '<div class="rent-fetch-options-sticky-wrap">';
- // add a wordpress save button here
submit_button();
+
+ echo '<ul class="rent-fetch-options-submenu">';
+
+ $active = ( 'data-sync' === $section ) ? 'tab-active' : '';
+ printf( '<li><a href="?page=rentfetch-options&tab=general§ion=data-sync" class="tab %s">Data Sync</a></li>', esc_html( $active ) );
+
+ $active = ( 'performance' === $section ) ? 'tab-active' : '';
+ printf( '<li><a href="?page=rentfetch-options&tab=general§ion=performance" class="tab %s">Performance</a></li>', esc_html( $active ) );
+
+ $active = ( 'analytics' === $section ) ? 'tab-active' : '';
+ printf( '<li><a href="?page=rentfetch-options&tab=general§ion=analytics" class="tab %s">Analytics</a></li>', esc_html( $active ) );
+
+ echo '</ul>';
echo '</div>';
echo '</div>';
-
+
echo '<div class="container">';
- ?>
- <div class="header">
- <h2 class="title">Rent Fetch General Settings</h2>
- <p class="description">Let’s get started. Select from the options below to configure Rent Fetch and any integrations.</p>
- </div>
+ if ( 'performance' === $section ) {
+ do_action( 'rentfetch_do_settings_general_performance' );
+ } elseif ( 'analytics' === $section ) {
+ do_action( 'rentfetch_do_settings_general_analytics' );
+ } else {
+ do_action( 'rentfetch_do_settings_general_data_sync' );
+ }
+ echo '</div><!-- .container -->';
+ echo '</section><!-- #rent-fetch-general-page -->';
+}
+add_action( 'rentfetch_do_settings_general', 'rentfetch_settings_general' );
- <div class="row">
- <div class="section">
- <!-- <div class="white-box"> -->
- <h2 class="title">Our premium availability syncing addon</h2>
- <p class="description">You can already manually enter data for as many properties, floorplans, and units as you'd like, and all layouts are enabled for this information.</p><p>However, if you'd like to automate the addition of properties and sync availability information hourly, we offer the <strong>Rent Fetch Sync</strong> addon to sync data with the Yardi/RentCafe, Realpage, Appfolio, and Entrata platforms. More information at <a href="https://rentfetch.io" target="_blank">rentfetch.io</a></p>
- <!-- </div> -->
- </div>
- </div>
- <?php
- do_action( 'rentfetch_do_settings_general_shared' );
+/**
+ * Add the notice about the sync functionality (this will be removed by the sync plugin if it's installed).
+ *
+ * @return void
+ */
+function rentfetch_settings_sync_functionality_notice() {
+ ?>
+ <div class="header">
+ <h2 class="title">Data Sync</h2>
+ <p class="description">Set up data sync settings and integrations for Rent Fetch.</p>
+ </div>
- echo '</div>';
- echo '</section><!-- #rent-fetch-general-page -->';
+ <div class="row">
+ <div class="section">
+ <h2 class="title">Automated Availability Sync Add-On</h2>
+ <p class="description">Rent Fetch already supports unlimited manual entry for properties, floor plans, and units, and all layouts work with manually entered data.</p>
+ <p>Need automation? <strong>Rent Fetch Sync</strong> can import properties and sync availability hourly with Yardi/RentCafe, RealPage, AppFolio, and Entrata. Learn more at <a href="https://rentfetch.io" target="_blank">rentfetch.io</a>.</p>
+ </div>
+ </div>
+ <?php
}
-add_action( 'rentfetch_do_settings_general', 'rentfetch_settings_sync_functionality_notice', 25 );
+add_action( 'rentfetch_do_settings_general_data_sync', 'rentfetch_settings_sync_functionality_notice', 25 );
/**
- * Add shared general settings
+ * Output the performance section of general settings.
*
* @return void
*/
-function rentfetch_settings_shared_general() {
+function rentfetch_settings_general_performance() {
?>
+ <div class="header">
+ <h2 class="title">Performance</h2>
+ <p class="description">Configure search result caching and search performance optimization settings.</p>
+ </div>
+
<div class="row">
<div class="section">
<label class="label-large">Search Result Caching</label>
@@ -114,6 +151,28 @@
outline: none;
box-shadow: none;
}
+ #rentfetch-popular-searches-container {
+ overflow-x: auto;
+ max-width: 100%;
+ }
+ #rentfetch-popular-searches-container table.rentfetch-popular-searches-table {
+ width: 100%;
+ table-layout: fixed;
+ margin: 0;
+ }
+ #rentfetch-popular-searches-container table.rentfetch-popular-searches-table th,
+ #rentfetch-popular-searches-container table.rentfetch-popular-searches-table td {
+ padding: 8px 10px;
+ vertical-align: top;
+ }
+ #rentfetch-popular-searches-container table.rentfetch-popular-searches-table th {
+ white-space: nowrap;
+ }
+ #rentfetch-popular-searches-container .rentfetch-search-query {
+ white-space: normal;
+ overflow-wrap: anywhere;
+ word-break: break-word;
+ }
</style>
<script>
@@ -201,28 +260,19 @@
nonce: '<?php echo esc_js( wp_create_nonce( 'rentfetch_popular_searches' ) ); ?>'
}, function(response) {
if (response.success && response.data.searches.length > 0) {
- var html = '<table class="widefat striped" style="border: none;"><thead><tr>';
- html += '<th style="width: 100px;">Type</th>';
+ var html = '<table class="widefat striped rentfetch-popular-searches-table" style="border: none;"><thead><tr>';
+ html += '<th style="width: 90px;">Type</th>';
html += '<th>Search Query</th>';
- html += '<th style="width: 120px; text-align: center;">Times Hit</th>';
- html += '<th style="width: 140px;">Last Executed</th>';
+ html += '<th style="width: 110px; text-align: center;">Times Hit</th>';
+ html += '<th style="width: 130px;">Last Executed</th>';
html += '</tr></thead><tbody>';
$.each(response.data.searches, function(index, search) {
- var decodedQuery = decodeURIComponent(search.query.replace(/+/g, ' '));
- var displayQuery = decodedQuery;
-
- // If query is empty or only has availability, show as "all available"
- if (decodedQuery === '' || decodedQuery === 'availability=1') {
- displayQuery = '(all available)';
- } else {
- // Remove availability parameter from other queries for cleaner display
- displayQuery = displayQuery.replace(/&?availability=[^&]*/g, '').replace(/^&/, '');
- }
-
+ var displayQuery = search.display_query || '';
+
html += '<tr>';
html += '<td><span style="display: inline-block; padding: 3px 8px; background: #f0f0f1; border-radius: 3px; font-size: 11px; text-transform: uppercase; font-weight: 600; color: #2c3338;">' + search.type + '</span></td>';
- html += '<td style="font-family: Consolas, Monaco, monospace; font-size: 12px; color: #50575e;">' + displayQuery + '</td>';
+ html += '<td class="rentfetch-search-query" style="font-family: Consolas, Monaco, monospace; font-size: 12px; color: #50575e;">' + displayQuery + '</td>';
html += '<td style="text-align: center; font-weight: 600; color: #2271b1;">' + search.count + '</td>';
html += '<td style="color: #646970; font-size: 13px;">' + search.last_used + '</td>';
html += '</tr>';
@@ -304,33 +354,55 @@
</div>
<?php
}
-add_action( 'rentfetch_do_settings_general_shared', 'rentfetch_settings_shared_general' );
+add_action( 'rentfetch_do_settings_general_performance', 'rentfetch_settings_general_performance' );
/**
- * Save the general settings
+ * Output the analytics section of general settings.
+ *
+ * @return void
*/
-function rentfetch_save_settings_general() {
-
- // Get the tab and section.
- $tab = rentfetch_settings_get_tab();
- $section = rentfetch_settings_get_section();
-
- // this particular settings page has no tab or section, and it's the only one that doesn't.
- if ( $tab || $section ) {
- return;
- }
+function rentfetch_settings_general_analytics() {
+ ?>
+ <div class="header">
+ <h2 class="title">Analytics</h2>
+ <p class="description">Enable or disable analytics event tracking for Rent Fetch templates.</p>
+ </div>
- $nonce = isset( $_POST['rentfetch_main_options_nonce_field'] ) ? sanitize_text_field( wp_unslash( $_POST['rentfetch_main_options_nonce_field'] ) ) : '';
+ <div class="row">
+ <div class="section">
+ <label class="label-large">Analytics</label>
+ <p class="description">Enable or disable analytics event tracking for Rent Fetch templates. When enabled, events are sent to any existing Google Analytics or Tag Manager setup found on the site.</p>
- // * Verify the nonce
- if ( ! wp_verify_nonce( wp_unslash( $nonce ), 'rentfetch_main_options_nonce_action' ) ) {
- die( 'Security check failed' );
- }
+ <ul class="checkboxes">
+ <li>
+ <label for="rentfetch_options_enable_analytics">
+ <input type="checkbox" name="rentfetch_options_enable_analytics" id="rentfetch_options_enable_analytics" <?php checked( get_option( 'rentfetch_options_enable_analytics', '1' ), '1' ); ?>>
+ Enable analytics tracking (recommended)
+ </label>
+ </li>
+ <li>
+ <label for="rentfetch_options_enable_analytics_debug">
+ <input type="checkbox" name="rentfetch_options_enable_analytics_debug" id="rentfetch_options_enable_analytics_debug" <?php checked( get_option( 'rentfetch_options_enable_analytics_debug', '0' ), '1' ); ?>>
+ Enable analytics debug overlay on click
+ </label>
+ </li>
+ </ul>
+ </div>
+ </div>
+ <?php
+}
+add_action( 'rentfetch_do_settings_general_analytics', 'rentfetch_settings_general_analytics' );
- // * When we save this particular batch of settings, we want to re-check the license
+/**
+ * Save general data sync settings.
+ *
+ * @return void
+ */
+function rentfetch_save_settings_general_data_sync() {
+ // * When we save this particular batch of settings, we want to re-check the license.
delete_transient( 'rentfetchsync_properties_limit' );
- // * When we save this particular batch of settings, we might be changing the sync settings, so we need to unschedule all the sync actions
+ // * When we save this particular batch of settings, we might be changing the sync settings, so we need to unschedule all the sync actions.
if ( function_exists( 'as_unschedule_all_actions' ) ) {
as_unschedule_all_actions( 'rfs_do_sync' );
as_unschedule_all_actions( 'rfs_yardi_do_delete_orphans' );
@@ -437,7 +509,6 @@
update_option( 'rentfetch_options_entrata_integration_creds_entrata_property_ids', $options_entrata_integration_creds_entrata_property_ids );
}
-
// Text field.
if ( isset( $_POST['rentfetch_options_rentmanager_integration_creds_rentmanager_companycode'] ) ) {
// Remove ".api.rentmanager.com" and anything that follows it.
@@ -448,8 +519,7 @@
}
if ( function_exists( 'rfs_get_rentmanager_properties_from_setting' ) ) {
- // this function is defined in the rentfetch-sync plugin, and allows for prefilling the properties for Rent Manager, where there are multiple locations possible.
- // and it's not feasible to have the user enter them all manually.
+ // This function is defined in the rentfetch-sync plugin, and allows for prefilling the properties for Rent Manager.
rfs_get_rentmanager_properties_from_setting();
}
@@ -488,24 +558,34 @@
update_option( 'rentfetch_options_appfolio_integration_creds_appfolio_property_ids', $options_appfolio_integration_creds_appfolio_property_ids );
}
- // Checkbox field - Enable query caching (inverted: checked = '0' for disable, unchecked = '1' for disable)
+ // * When we save this particular batch of settings, we want to always clear the transient that holds the API info.
+ delete_transient( 'rentfetch_api_info' );
+}
+
+/**
+ * Save general performance settings.
+ *
+ * @return void
+ */
+function rentfetch_save_settings_general_performance() {
+ // Checkbox field - Enable query caching (inverted: checked = '0' for disable, unchecked = '1' for disable).
$disable_query_caching = isset( $_POST['rentfetch_options_disable_query_caching'] ) ? '0' : '1';
update_option( 'rentfetch_options_disable_query_caching', $disable_query_caching );
- // Checkbox field - Enable cache warming (checked = '1', unchecked = '0')
- $enable_cache_warming = isset( $_POST['rentfetch_options_enable_cache_warming'] ) ? '1' : '0';
+ // Checkbox field - Enable cache warming (checked = '1', unchecked = '0').
+ $enable_cache_warming = isset( $_POST['rentfetch_options_enable_cache_warming'] ) ? '1' : '0';
$previous_cache_warming = get_option( 'rentfetch_options_enable_cache_warming', '0' );
update_option( 'rentfetch_options_enable_cache_warming', $enable_cache_warming );
- // Schedule or unschedule cache warming based on setting change
+ // Schedule or unschedule cache warming based on setting change.
if ( $enable_cache_warming !== $previous_cache_warming ) {
if ( function_exists( 'rentfetch_schedule_cache_warming' ) ) {
rentfetch_schedule_cache_warming();
}
}
- // If caching is disabled (value is '1'), clear existing transients
- if ( $disable_query_caching === '1' ) {
+ // If caching is disabled (value is '1'), clear existing transients.
+ if ( '1' === $disable_query_caching ) {
global $wpdb;
$transients = $wpdb->get_col( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '_transient_rentfetch_%'" );
foreach ( $transients as $transient ) {
@@ -514,23 +594,70 @@
}
}
- // Checkbox field - Enable search indexes
+ // Checkbox field - Enable search indexes.
$enable_search_indexes = isset( $_POST['rentfetch_options_enable_search_indexes'] ) ? '1' : '0';
$previous_value = get_option( 'rentfetch_options_enable_search_indexes', '1' );
update_option( 'rentfetch_options_enable_search_indexes', $enable_search_indexes );
- // If the setting changed, create or remove indexes accordingly
+ // If the setting changed, create or remove indexes accordingly.
if ( $enable_search_indexes !== $previous_value ) {
if ( '1' === $enable_search_indexes ) {
- // Create indexes
rentfetch_create_indexes();
} else {
- // Remove indexes
rentfetch_remove_indexes();
}
}
+}
- // * When we save this particular batch of settings, we want to always clear the transient that holds the API info.
- delete_transient( 'rentfetch_api_info' );
+/**
+ * Save general analytics settings.
+ *
+ * @return void
+ */
+function rentfetch_save_settings_general_analytics() {
+ // Checkbox field - Enable analytics.
+ $enable_analytics = isset( $_POST['rentfetch_options_enable_analytics'] ) ? '1' : '0';
+ update_option( 'rentfetch_options_enable_analytics', $enable_analytics );
+
+ // Checkbox field - Enable analytics debug overlay.
+ $enable_analytics_debug = isset( $_POST['rentfetch_options_enable_analytics_debug'] ) ? '1' : '0';
+ update_option( 'rentfetch_options_enable_analytics_debug', $enable_analytics_debug );
+}
+
+/**
+ * Save the general settings.
+ *
+ * @return void
+ */
+function rentfetch_save_settings_general() {
+ $tab = rentfetch_settings_get_tab();
+ $section = rentfetch_settings_get_section();
+
+ if ( $tab && 'general' !== $tab ) {
+ return;
+ }
+
+ if ( ! $section ) {
+ $section = 'data-sync';
+ }
+
+ if ( ! in_array( $section, array( 'data-sync', 'performance', 'analytics' ), true ) ) {
+ return;
+ }
+
+ $nonce = isset( $_POST['rentfetch_main_options_nonce_field'] ) ? sanitize_text_field( wp_unslash( $_POST['rentfetch_main_options_nonce_field'] ) ) : '';
+
+ // * Verify the nonce.
+ if ( ! wp_verify_nonce( wp_unslash( $nonce ), 'rentfetch_main_options_nonce_action' ) ) {
+ die( 'Security check failed' );
+ }
+
+ if ( 'performance' === $section ) {
+ rentfetch_save_settings_general_performance();
+ } elseif ( 'analytics' === $section ) {
+ rentfetch_save_settings_general_analytics();
+ } else {
+ rentfetch_save_settings_general_data_sync();
+ }
}
-add_action( 'rentfetch_save_settings', 'rentfetch_save_settings_general' );
No newline at end of file
+add_action( 'rentfetch_save_settings', 'rentfetch_save_settings_general' );
--- a/rentfetch/lib/admin/search-analytics.php
+++ b/rentfetch/lib/admin/search-analytics.php
@@ -10,6 +10,31 @@
}
/**
+ * Sanitize search parameters for analytics storage/output.
+ *
+ * @param array $params Search parameters.
+ * @return array Sanitized parameters.
+ */
+function rentfetch_sanitize_search_params( $params ) {
+ $sanitized = array();
+
+ foreach ( $params as $key => $value ) {
+ if ( is_array( $value ) ) {
+ $sanitized[ $key ] = rentfetch_sanitize_search_params( $value );
+ continue;
+ }
+
+ if ( is_object( $value ) ) {
+ continue;
+ }
+
+ $sanitized[ $key ] = sanitize_text_field( wp_unslash( (string) $value ) );
+ }
+
+ return $sanitized;
+}
+
+/**
* Track search queries for analytics
*
* @param string $search_type Type of search (properties or floorplans).
@@ -31,6 +56,9 @@
ARRAY_FILTER_USE_BOTH
);
+ $clean_params = rentfetch_sanitize_search_params( $clean_params );
+ $clean_params = array_filter( $clean_params );
+
// Sort params for consistent cache keys.
ksort( $clean_params );
--- a/rentfetch/lib/common/analytics-events.php
+++ b/rentfetch/lib/common/analytics-events.php
@@ -0,0 +1,203 @@
+<?php
+/**
+ * Analytics event helpers for Rent Fetch.
+ *
+ * @package rentfetch
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+ exit; // Exit if accessed directly.
+}
+
+/**
+ * Build data attributes for analytics context (no event name).
+ *
+ * @param array $context Context values (property_name, property_city, floorplan_name).
+ * @return string Data attributes.
+ */
+function rentfetch_get_tracking_context_attributes( $context = array() ) {
+ $attributes = array();
+
+ if ( ! empty( $context['property_id'] ) ) {
+ $attributes['data-rentfetch-property-id'] = $context['property_id'];
+ }
+
+ if ( ! empty( $context['property_name'] ) ) {
+ $attributes['data-rentfetch-property-name'] = $context['property_name'];
+ }
+
+ if ( ! empty( $context['property_city'] ) ) {
+ $attributes['data-rentfetch-property-city'] = $context['property_city'];
+ }
+
+ if ( ! empty( $context['floorplan_id'] ) ) {
+ $attributes['data-rentfetch-floorplan-id'] = $context['floorplan_id'];
+ }
+
+ if ( ! empty( $context['floorplan_name'] ) ) {
+ $attributes['data-rentfetch-floorplan-name'] = $context['floorplan_name'];
+ }
+
+ $output = '';
+ foreach ( $attributes as $key => $value ) {
+ $output .= sprintf( ' %s="%s"', $key, esc_attr( $value ) );
+ }
+
+ return $output;
+}
+
+/**
+ * Build data attributes for analytics events.
+ *
+ * @param string $event_name Event name (prefixed with rentfetch_).
+ * @param array $context Context values (property_name, property_city, floorplan_name).
+ * @return string Data attributes.
+ */
+function rentfetch_get_tracking_data_attributes( $event_name, $context = array() ) {
+ if ( empty( $event_name ) ) {
+ return '';
+ }
+
+ if ( 0 !== strpos( $event_name, 'rentfetch_' ) ) {
+ $event_name = 'rentfetch_' . ltrim( $event_name, '_' );
+ }
+
+ $attributes = array(
+ 'data-rentfetch-event' => $event_name,
+ );
+
+ if ( ! empty( $context['property_id'] ) ) {
+ $attributes['data-rentfetch-property-id'] = $context['property_id'];
+ }
+
+ if ( ! empty( $context['property_name'] ) ) {
+ $attributes['data-rentfetch-property-name'] = $context['property_name'];
+ }
+
+ if ( ! empty( $context['property_city'] ) ) {
+ $attributes['data-rentfetch-property-city'] = $context['property_city'];
+ }
+
+ if ( ! empty( $context['floorplan_id'] ) ) {
+ $attributes['data-rentfetch-floorplan-id'] = $context['floorplan_id'];
+ }
+
+ if ( ! empty( $context['floorplan_name'] ) ) {
+ $attributes['data-rentfetch-floorplan-name'] = $context['floorplan_name'];
+ }
+
+ $output = '';
+ foreach ( $attributes as $key => $value ) {
+ $output .= sprintf( ' %s="%s"', $key, esc_attr( $value ) );
+ }
+
+ return $output;
+}
+
+/**
+ * Build tracking context for a property.
+ *
+ * @param string|null $property_id Optional property_id meta value.
+ * @param int|null $post_id Optional property post ID.
+ * @return array
+ */
+function rentfetch_get_property_tracking_context( $property_id = null, $post_id = null ) {
+ if ( $property_id ) {
+ $post_id = rentfetch_get_post_id_from_property_id( $property_id );
+ }
+
+ if ( ! $post_id ) {
+ $post_id = get_the_ID();
+ }
+
+ if ( ! $post_id ) {
+ $post_id = get_queried_object_id();
+ }
+
+ if ( ! $post_id ) {
+ return array();
+ }
+
+ $property_name = get_the_title( $post_id );
+ $property_city = get_post_meta( $post_id, 'city', true );
+ $property_id_meta = get_post_meta( $post_id, 'property_id', true );
+
+ return array(
+ 'property_id' => $property_id_meta ? sanitize_text_field( $property_id_meta ) : null,
+ 'property_name' => $property_name ? sanitize_text_field( $property_name ) : null,
+ 'property_city' => $property_city ? sanitize_text_field( $property_city ) : null,
+ );
+}
+
+/**
+ * Build tracking context for a floorplan.
+ *
+ * @param int|null $floorplan_id Optional floorplan post ID.
+ * @return array
+ */
+function rentfetch_get_floorplan_tracking_context( $floorplan_id = null ) {
+ if ( ! $floorplan_id ) {
+ $floorplan_id = get_the_ID();
+ }
+
+ if ( ! $floorplan_id ) {
+ $floorplan_id = get_queried_object_id();
+ }
+
+ if ( ! $floorplan_id ) {
+ return array();
+ }
+
+ $floorplan_name = get_the_title( $floorplan_id );
+ $floorplan_id_meta = get_post_meta( $floorplan_id, 'floorplan_id', true );
+ $property_id = get_post_meta( $floorplan_id, 'property_id', true );
+ $property_post = $property_id ? rentfetch_get_post_id_from_property_id( $property_id ) : null;
+
+ $property_name = $property_post ? get_the_title( $property_post ) : null;
+ $property_city = $property_post ? get_post_meta( $property_post, 'city', true ) : null;
+
+ return array(
+ 'property_id' => $property_id ? sanitize_text_field( $property_id ) : null,
+ 'property_name' => $property_name ? sanitize_text_field( $property_name ) : null,
+ 'property_city' => $property_city ? sanitize_text_field( $property_city ) : null,
+ 'floorplan_id' => $floorplan_id_meta ? sanitize_text_field( $floorplan_id_meta ) : null,
+ 'floorplan_name' => $floorplan_name ? sanitize_text_field( $floorplan_name ) : null,
+ );
+}
+
+/**
+ * Enqueue analytics events script on single property/floorplan pages.
+ *
+ * @return void
+ */
+function rentfetch_enqueue_analytics_events_script() {
+ $enabled = get_option( 'rentfetch_options_enable_analytics', '1' );
+
+ if ( '1' !== $enabled ) {
+ return;
+ }
+
+ if ( is_singular( array( 'properties', 'floorplans' ) ) ) {
+ $debug_option = get_option( 'rentfetch_options_enable_analytics_debug', '0' );
+ $debug_enabled = in_array( $debug_option, array( '1', 1, true, 'true', 'yes' ), true );
+ $debug_override = isset( $_GET['rentfetch_debug'] ) ? sanitize_text_field( wp_unslash( $_GET['rentfetch_debug'] ) ) : '';
+
+ if ( $debug_override !== '' ) {
+ $debug_enabled = in_array( $debug_override, array( '1', 'true', 'yes', 'on' ), true );
+ }
+
+ wp_enqueue_script( 'rentfetch-analytics-events' );
+ $debug_allowed = is_user_logged_in() && current_user_can( 'manage_options' );
+
+ wp_localize_script(
+ 'rentfetch-analytics-events',
+ 'rentfetchAnalyticsSettings',
+ array(
+ 'enabled' => ( '1' === $enabled ),
+ 'debug' => $debug_enabled,
+ 'debugAllowed' => $debug_allowed,
+ )
+ );
+ }
+}
+add_action( 'wp_enqueue_scripts', 'rentfetch_enqueue_analytics_events_script' );
--- a/rentfetch/lib/common/functions-floorplans.php
+++ b/rentfetch/lib/common/functions-floorplans.php
@@ -488,13 +488,14 @@
$link = get_post_meta( get_the_ID(), 'availability_url', true );
$target = rentfetch_get_link_target( $link );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_applynow_click', rentfetch_get_floorplan_tracking_context() );
// bail if no link is set.
if ( false === $link || empty( $link ) ) {
return false;
}
- return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-availability-button">%s</a>', $link, $target, $button_label );
+ return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-availability-button"%s>%s</a>', $link, $target, $tracking_attrs, $button_label );
}
add_filter( 'rentfetch_floorplan_default_availability_button_markup', 'rentfetch_floorplan_default_availability_button_markup' );
@@ -535,13 +536,14 @@
$link = get_option( 'rentfetch_options_unavailability_button_link' );
$target = rentfetch_get_link_target( $link );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_unavailability_click', rentfetch_get_floorplan_tracking_context() );
// bail if no link is set.
if ( false === $link || empty( $link ) ) {
return false;
}
- return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-unavailability-button">%s</a>', $link, $target, $button_label );
+ return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-unavailability-button"%s>%s</a>', $link, $target, $tracking_attrs, $button_label );
}
add_filter( 'rentfetch_floorplan_default_unavailability_button_markup', 'rentfetch_floorplan_default_unavailability_button_markup' );
@@ -573,8 +575,9 @@
$button_label = get_option( 'rentfetch_options_contact_button_button_label', 'Contact' );
$link = get_option( 'rentfetch_options_contact_button_link', false );
$target = rentfetch_get_link_target( $link );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_contact_click', rentfetch_get_floorplan_tracking_context() );
- return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-contact-button">%s</a>', $link, $target, $button_label );
+ return sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-contact-button"%s>%s</a>', $link, $target, $tracking_attrs, $button_label );
}
add_filter( 'rentfetch_filter_floorplan_default_contact_button_markup', 'rentfetch_floorplan_default_contact_button_markup' );
@@ -595,7 +598,8 @@
return;
}
- $button = sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-tour-button">%s</a>', $fallback_link, $target, $label );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_scheduletour_click', rentfetch_get_floorplan_tracking_context() );
+ $button = sprintf( '<a href="%s" target="%s" class="rentfetch-button rentfetch-floorplan-tour-button"%s>%s</a>', $fallback_link, $target, $tracking_attrs, $label );
echo wp_kses_post( apply_filters( 'rentfetch_floorplan_default_tour_button', $button ) );
}
@@ -1183,4 +1187,4 @@
// if the embed is not empty, return the embed.
return $embed;
-}
No newline at end of file
+}
--- a/rentfetch/lib/common/functions-properties.php
+++ b/rentfetch/lib/common/functions-properties.php
@@ -355,8 +355,9 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_directions_click', rentfetch_get_property_tracking_context( $property_id ) );
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 location-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M15 10.5a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" /><path stroke-linecap="round" stroke-linejoin="round" d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0Z" /></svg>';
- $location_button = sprintf( '<a class="%s" href="%s" target="_blank">%sGet Directions</a>', $classes, esc_url( $location_link ), $svg );
+ $location_button = sprintf( '<a class="%s" href="%s" target="_blank"%s>%sGet Directions</a>', $classes, esc_url( $location_link ), $tracking_attrs, $svg );
return apply_filters( 'rentfetch_filter_property_location_button', $location_button, $property_id, $class );
}
@@ -547,8 +548,9 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_phonecall_click', rentfetch_get_property_tracking_context( $property_id ) );
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 phone-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M10.5 1.5H8.25A2.25 2.25 0 0 0 6 3.75v16.5a2.25 2.25 0 0 0 2.25 2.25h7.5A2.25 2.25 0 0 0 18 20.25V3.75a2.25 2.25 0 0 0-2.25-2.25H13.5m-3 0V3h3V1.5m-3 0h3m-3 18.75h3" /></svg>';
- $phone_button = sprintf( '<a class="%s" href="tel:%s">%s%s</a>', $classes, esc_html( $phone_link ), $svg, esc_html( $phone ) );
+ $phone_button = sprintf( '<a class="%s" href="tel:%s"%s>%s%s</a>', $classes, esc_html( $phone_link ), $tracking_attrs, $svg, esc_html( $phone ) );
if ( $phone ) {
return apply_filters( 'rentfetch_filter_property_phone_button', $phone_button, $property_id, $class );
@@ -672,8 +674,9 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_visitpropertywebsite_click', rentfetch_get_property_tracking_context( $property_id ) );
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 website-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25" /></svg>';
- $website_button = sprintf( '<a class="%s" href="%s" target="%s">%sVisit Website</a>', $classes, esc_html( $url ), esc_attr( $target ), $svg );
+ $website_button = sprintf( '<a class="%s" href="%s" target="%s"%s>%sVisit Website</a>', $classes, esc_html( $url ), esc_attr( $target ), $tracking_attrs, $svg );
if ( $url ) {
return apply_filters( 'rentfetch_filter_property_website', $website_button, $property_id, $class );
@@ -733,8 +736,9 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_emailus_click', rentfetch_get_property_tracking_context( $property_id ) );
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 email-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 0 1-2.25 2.25h-15a2.25 2.25 0 0 1-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0 0 19.5 4.5h-15a2.25 2.25 0 0 0-2.25 2.25m19.5 0v.243a2.25 2.25 0 0 1-1.07 1.916l-7.5 4.615a2.25 2.25 0 0 1-2.36 0L3.32 8.91a2.25 2.25 0 0 1-1.07-1.916V6.75" /></svg>';
- $contact_button = sprintf( '<a class="%s" href="%s">%sEmail Us</a>', $classes, esc_html( $email_link ), $svg );
+ $contact_button = sprintf( '<a class="%s" href="%s"%s>%sEmail Us</a>', $classes, esc_html( $email_link ), $tracking_attrs, $svg );
$email_button = apply_filters( 'rentfetch_filter_property_contact_button', $contact_button, $property_id, $class );
if ( $email ) {
@@ -892,7 +896,8 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
- $embedlink = sprintf( '<a class="%s" data-gallery="post-%s" data-glightbox="type: video;" href="%s">%s%s</a>', $classes, $post_id, $oembedlink, $svg, $tour_link_text );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_tour_click', rentfetch_get_property_tracking_context( $property_id, $post_id ) );
+ $embedlink = sprintf( '<a class="%s" data-gallery="post-%s" data-glightbox="type: video;" href="%s"%s>%s%s</a>', $classes, $post_id, $oembedlink, $tracking_attrs, $svg, $tour_link_text );
}
$matterport_pattern = '/src="([^"]*matterport[^"]*)"/i'; // Added "matterport" to the pattern.
@@ -910,7 +915,8 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
- $embedlink = sprintf( '<a class="%s" data-gallery="post-%s" href="%s">%s%s</a>', $classes, $post_id, $oembedlink, $svg, $tour_link_text );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_tour_click', rentfetch_get_property_tracking_context( $property_id, $post_id ) );
+ $embedlink = sprintf( '<a class="%s" data-gallery="post-%s" href="%s"%s>%s%s</a>', $classes, $post_id, $oembedlink, $tracking_attrs, $svg, $tour_link_text );
}
// if it's anything else (like just an oembed, including an oembed for either matterport or youtube).
@@ -920,7 +926,8 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
- $embedlink = sprintf( '<a class="%s" target="_blank" data-gallery="post-%s" href="%s">%s%s</a>', $classes, $post_id, $oembedlink, $svg, $tour_link_text );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_tour_click', rentfetch_get_property_tracking_context( $property_id, $post_id ) );
+ $embedlink = sprintf( '<a class="%s" target="_blank" data-gallery="post-%s" href="%s"%s>%s%s</a>', $classes, $post_id, $oembedlink, $tracking_attrs, $svg, $tour_link_text );
}
return apply_filters( 'rentfetch_filter_property_tour_button', $embedlink, $property_id, $class );
@@ -988,8 +995,9 @@
if ( ! empty( $class ) ) {
$classes .= ' ' . esc_attr( $class );
}
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_scheduletour_click', rentfetch_get_property_tracking_context( $property_id ) );
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 tour-booking-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M6.75 3v2.25M17.25 3v2.25M3 18.75V7.5a2.25 2.25 0 0 1 2.25-2.25h13.5A2.25 2.25 0 0 1 21 7.5v11.25m-18 0A2.25 2.25 0 0 0 5.25 21h13.5A2.25 2.25 0 0 0 21 18.75m-18 0v-7.5A2.25 2.25 0 0 1 5.25 9h13.5A2.25 2.25 0 0 1 21 11.25v7.5m-9-6h.008v.008H12v-.008ZM12 15h.008v.008H12V15Zm0 2.25h.008v.008H12v-.008ZM9.75 15h.008v.008H9.75V15Zm0 2.25h.008v.008H9.75v-.008ZM7.5 15h.008v.008H7.5V15Zm0 2.25h.008v.008H7.5v-.008Zm6.75-4.5h.008v.008h-.008v-.008Zm0 2.25h.008v.008h-.008V15Zm0 2.25h.008v.008h-.008v-.008Zm2.25-4.5h.008v.008H16.5v-.008Zm0 2.25h.008v.008H16.5V15Z" /></svg>';
- $tour_booking_button = sprintf( '<a class="%s" href="%s" target="%s">%sBook Tour</a>', $classes, esc_html( $url ), esc_attr( $target ), $svg );
+ $tour_booking_button = sprintf( '<a class="%s" href="%s" target="%s"%s>%sBook Tour</a>', $classes, esc_html( $url ), esc_attr( $target ), $tracking_attrs, $svg );
if ( $url ) {
return apply_filters( 'rentfetch_filter_property_tour_booking', $tour_booking_button, $property_id, $class );
@@ -1046,6 +1054,7 @@
$classes .= ' ' . esc_attr( $class );
}
$svg = '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6 office-hours-icon"><path stroke-linecap="round" stroke-linejoin="round" d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" /></svg>';
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_officehours_click', rentfetch_get_property_tracking_context( $property_id ) );
// Get office hours markup without heading and wrapper
$days = array( 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday' );
@@ -1065,10 +1074,11 @@
$office_hours_button = sprintf(
'<details class="office-hours-details">
- <summary class="%s">%sOffice Hours</summary>
+ <summary class="%s"%s>%sOffice Hours</summary>
<div class="office-hours-content">%s</div>
</details>',
$classes,
+ $tracking_attrs,
$svg,
$office_hours_content
);
@@ -1104,6 +1114,10 @@
),
'summary' => array(
'class' => true,
+ 'data-rentfetch-event' => true,
+ 'data-rentfetch-property-id' => true,
+ 'data-rentfetch-property-name' => true,
+ 'data-rentfetch-property-city' => true,
),
'div' => array(
'class' => true,
@@ -2085,4 +2099,4 @@
if ( $office_hours ) {
echo wp_kses_post( $office_hours );
}
-}
No newline at end of file
+}
--- a/rentfetch/lib/common/functions-units.php
+++ b/rentfetch/lib/common/functions-units.php
@@ -240,7 +240,8 @@
$apply_online_url = get_post_meta( get_the_ID(), 'apply_online_url', true );
if ( $apply_online_url ) {
- $markup = sprintf( '<a href="%s" class="rentfetch-button rentfetch-button-small" target="_blank">Apply Online</a>', $apply_online_url );
+ $tracking_attrs = rentfetch_get_tracking_data_attributes( 'rentfetch_applyonline_click', rentfetch_get_floorplan_tracking_context() );
+ $markup = sprintf( '<a href="%s" class="rentfetch-button rentfetch-button-small" target="_blank"%s>Apply Online</a>', $apply_online_url, $tracking_attrs );
echo wp_kses_post( apply_filters( 'rentfetch_filter_unit_apply_button_markup', $markup ) );
} else {
rentfetch_unit_default_contact_button();
--- a/rentfetch/lib/common/get-meta-values.php
+++ b/rentfetch/lib/common/get-meta-values.php
@@ -50,6 +50,14 @@
)
);
+ // Sanitize meta values before caching.
+ $r = array_map(
+ function( $value ) {
+ return sanitize_text_field( wp_unslash( $value ) );
+ },
+ (array) $r
+ );
+
// Cache the result briefly to avoid repeated expensive queries.
if ( get_option( 'rentfetch_options_disable_query_caching' ) !== '1' ) {
set_transient( $cache_key, $r, 5 * MINUTE_IN_SECONDS );
--- a/rentfetch/lib/initialization/enqueue.php
+++ b/rentfetch/lib/initialization/enqueue.php
@@ -86,6 +86,9 @@
// Property fees tooltip.
wp_register_script( 'rentfetch-property-fees-tooltip', RENTFETCH_PATH . 'js/rentfetch-property-fees-tooltip.js', array( 'jquery' ), RENTFETCH_VERSION, true );
+
+ // Analytics events.
+ wp_register_script( 'rentfetch-analytics-events', RENTFETCH_PATH . 'js/rentfetch-analytics-events.js', array(), RENTFETCH_VERSION, true );
}
add_action( 'wp_enqueue_scripts', 'rentfetch_enqueue_scripts_stylesheets' );
--- a/rentfetch/lib/shortcode/search-filters/floorplan-filters/baths.php
+++ b/rentfetch/lib/shortcode/search-filters/floorplan-filters/baths.php
@@ -50,8 +50,8 @@
%s />
<span>%s</span>
</label>',
- wp_kses_post( $bath ),
- wp_kses_post( $bath ),
+ esc_attr( $bath ),
+ esc_attr( $bath ),
$checked ? 'checked' : '', // Apply checked attribute.
wp_kses_post( $label )
);
--- a/rentfetch/lib/shortcode/search-filters/property-filters/pets.php
+++ b/rentfetch/lib/shortcode/search-filters/property-filters/pets.php
@@ -51,7 +51,7 @@
printf( '<button type="button" class="toggle">%s</button>', esc_html( $label ) );
echo '<div class="input-wrap checkboxes">';
foreach ( $pets as $pet ) {
- printf( '<label><input type="radio" data-pets="%s" data-pets-name="%s" name="pets" value="%s" /><span>%s</span></label>', esc_html( $pet ), esc_html( $pets_choices[ $pet ] ), esc_html( $pet ), esc_html( $pets_choices[ $pet ] ) );
+ printf( '<label><input type="radio" data-pets="%s" data-pets-name="%s" name="pets" value="%s" /><span>%s</span></label>', esc_attr( $pet ), esc_attr( $pets_choices[ $pet ] ), esc_attr( $pet ), esc_html( $pets_choices[ $pet ] ) );
}
echo '</div>'; // .checkboxes.
echo '</fieldset>';
--- a/rentfetch/rentfetch.php
+++ b/rentfetch/rentfetch.php
@@ -9,7 +9,7 @@
* Plugin Name: Rent Fetch
* Plugin URI: http://wordpress.org/plugins/rentfetch/
* Description: Displays searchable rental properties, floorplans, and unit availability.
- * Version: 0.32.6
+ * Version: 0.32.7
* Author: Brindle Digital
* Author URI: https://www.brindledigital.com
* Text Domain: rentfetch
@@ -23,7 +23,7 @@
}
// Define the version of the plugin.
-define( 'RENTFETCH_VERSION', '0.32.6' );
+define( 'RENTFETCH_VERSION', '0.32.7' );
// Set up plugin directories.
define( 'RENTFETCH_DIR', plugin_dir_path( __FILE__ ) );
--- a/rentfetch/template/single-floorplans.php
+++ b/rentfetch/template/single-floorplans.php
@@ -30,8 +30,10 @@
$pricing = rentfetch_get_floorplan_pricing();
$units_count = rentfetch_get_floorplan_units_count_from_cpt();
$description = rentfetch_get_floorplan_description();
+ $tracking_context = rentfetch_get_floorplan_tracking_context( get_the_ID() );
+ $tracking_context_attrs = rentfetch_get_tracking_context_attributes( $tracking_context );
- echo '<div class="single-floorplans-container-outer container-current-floorplan-info">';
+ printf( '<div class="single-floorplans-container-outer container-current-floorplan-info"%s>', $tracking_context_attrs );
echo '<div class="single-floorplans-container-inner">';
echo '<div class="current-floorplan-info">';
--- a/rentfetch/template/single-properties.php
+++ b/rentfetch/template/single-properties.php
@@ -12,7 +12,9 @@
get_header();
// * Markup.
-echo '<div class="single-properties-wrap">';
+$tracking_context = rentfetch_get_property_tracking_context( null, get_queried_object_id() );
+$tracking_context_attrs = rentfetch_get_tracking_context_attributes( $tracking_context );
+printf( '<div class="single-properties-wrap"%s>', $tracking_context_attrs );
do_action( 'rentfetch_do_single_properties_parts' );