Plugin Directory

Changeset 3487409


Ignore:
Timestamp:
03/20/2026 06:47:46 PM (8 days ago)
Author:
rocketcomunicazione
Message:

2.4.7

Location:
rc-site-manager-optimization/trunk
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • rc-site-manager-optimization/trunk/assets/scripts/script.js

    r3482178 r3487409  
    408408                : 'Copied!';
    409409            const tooltip = $(`<span class="rc_sm_tooltip_copy_action">${copiedText}</span>`);
    410             $btn.after(tooltip);
     410            if ($btn.hasClass('rc_sm_tooltip_sx')) {
     411                $btn.before(tooltip);
     412            } else {
     413                $btn.after(tooltip);
     414            }
    411415            setTimeout(() => {
    412416                tooltip.fadeOut(300, () => tooltip.remove());
  • rc-site-manager-optimization/trunk/assets/styles/style.css

    r3485061 r3487409  
    413413    font-size: 12px;
    414414    margin-left: 5px;
     415    margin-right: 5px;
    415416    z-index: 1000;
    416417    white-space: nowrap;
  • rc-site-manager-optimization/trunk/crons/pagespeed.php

    r3482478 r3487409  
    133133        'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=%s&key=%s&strategy=%s&category=PERFORMANCE&category=ACCESSIBILITY&category=BEST_PRACTICES&category=SEO',
    134134        urlencode($url),
    135         RC_SM_PAGESPEED_API_KEY,
     135        RC_SM_API_KEY_GOOGLE_CLOUD,
    136136        $strategy
    137137    );
  • rc-site-manager-optimization/trunk/rc-site-manager-optimization.php

    r3487089 r3487409  
    33 * Plugin Name: RC Site Manager & Optimization
    44 * Description: All-in-one Wordpress manager: control WooCommerce, SEO, caching, media, and multilingual tools from one dashboard.
    5  * Version: 2.4.6
     5 * Version: 2.4.7
    66 * Author: Rocket Comunicazione
    77 * Author URI: https://www.rocketcomunicazione.com
     
    1414
    1515
    16 define('RC_SM_PLUGIN_VERSION', '2.4.6');
     16define('RC_SM_PLUGIN_VERSION', '2.4.7');
    1717
    1818define( 'RC_SM_SITE_URL', home_url() );
     
    4141include plugin_dir_path(__FILE__) . 'includes/functions/index.php';
    4242include plugin_dir_path(__FILE__) . 'includes/security.php';
     43include plugin_dir_path(__FILE__) . 'includes/database.php';
    4344
    4445define('RC_SM_PAGINATION_ORDER_OFFSET', 50);
     
    4849define('RC_SM_TABLE_PREMIUM_CUSTOM_SOFTWARE', $wpdb->prefix . 'rc_sm_tb_premium_custom_software');
    4950define('RC_SM_TABLE_PAGESPEED', $wpdb->prefix . 'rc_sm_tb_pagespeed');
    50 define('RC_SM_PAGESPEED_API_KEY', 'AIzaSyAou3gnyHfrzOQeXNin-vG7KGDXLRmHxT4');
    51 
    52 if (RC_SM_SITE_URL === 'https://www.rocketcomunicazione.com') {
    53     define('RC_SM_TABLE_AGENCY_CONTROL', $wpdb->prefix . 'rc_sm_tb_agency_control');
    54 }
     51
     52define( 'RC_SM_API_KEY_GOOGLE_CLOUD', rc_sm_tb_settings_value( 'google_cloud', 'api_keys' )['key'] ?? '' );
    5553
    5654define('RC_SM_YEAR_START_SUBSCRIBER', rc_sm_fn_year_start('subscriber'));
     
    5856define('RC_SM_YEAR_START_ORDER', rc_sm_fn_year_start('order'));
    5957
    60 
    61 include plugin_dir_path(__FILE__) . 'includes/database.php';
     58if (RC_SM_SITE_URL === 'https://www.rocketcomunicazione.com') {
     59    define('RC_SM_TABLE_AGENCY_CONTROL', $wpdb->prefix . 'rc_sm_tb_agency_control');
     60}
     61
    6262include plugin_dir_path(__FILE__) . 'includes/language.php';
    63 
    64 
    6563include plugin_dir_path(__FILE__) . 'crons/index.php';
    6664
  • rc-site-manager-optimization/trunk/readme.txt

    r3487089 r3487409  
    55Tested up to: 6.9
    66Requires PHP: 8.1
    7 Stable tag: 2.4.6
     7Stable tag: 2.4.7
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    160160== Changelog ==
    161161
     162
     163= 2.4.7 - March 20, 2026 =
     164* Minor bug fixes and improvement
    162165
    163166= 2.4.6 - March 20, 2026 =
     
    316319== Upgrade Notice ==
    317320
     321= 2.4.7 - March 20, 2026 =
     322Minor bug fixes.
     323
    318324= 2.4.6 - March 20, 2026 =
    319325Minor bug fixes.
  • rc-site-manager-optimization/trunk/settings/settings_function.php

    r3438010 r3487409  
    210210    wp_die();
    211211});
     212
     213// ─── AJAX: verifica API Key ───────────────────────────────────────────────────
     214
     215add_action('wp_ajax_rc_sm_api_key_verify', function () {
     216
     217    if (!rc_sm_security_ajax_verify()) return;
     218
     219    $slug    = rc_sm_security_ajax_get_param('slug', '');
     220    $api_key = rc_sm_security_ajax_get_param('api_key', '');
     221
     222    if (empty($slug) || empty($api_key)) {
     223        wp_send_json_error(array('message' => __('Missing required parameters.', 'rc-site-manager-optimization')));
     224        return;
     225    }
     226
     227    if ($slug === 'google_cloud') {
     228
     229        $response = wp_remote_get(
     230            add_query_arg(array(
     231                'url'      => home_url('/'),
     232                'key'      => $api_key,
     233                'strategy' => 'mobile',
     234                'category' => 'performance',
     235            ), 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed'),
     236            array('timeout' => 20)
     237        );
     238
     239        if (is_wp_error($response)) {
     240            wp_send_json_error(array('message' => __('Connection error. Please try again.', 'rc-site-manager-optimization')));
     241            return;
     242        }
     243
     244        $body      = json_decode(wp_remote_retrieve_body($response), true);
     245        $http_code = (int) wp_remote_retrieve_response_code($response);
     246
     247        if ($http_code === 200 && isset($body['lighthouseResult'])) {
     248            rc_sm_api_key_save($slug, $api_key);
     249            wp_send_json_success(array('message' => __('API Key valid and active.', 'rc-site-manager-optimization')));
     250        } else {
     251            $error_msg = isset($body['error']['message'])
     252                ? $body['error']['message']
     253                : __('Invalid API Key.', 'rc-site-manager-optimization');
     254            rc_sm_api_key_delete($slug);
     255            wp_send_json_error(array('message' => $error_msg));
     256        }
     257
     258    } else {
     259        wp_send_json_error(array('message' => __('Unknown API Key type.', 'rc-site-manager-optimization')));
     260    }
     261
     262    wp_die();
     263});
     264
     265
     266// ─── Helpers API Keys ─────────────────────────────────────────────────────────
     267
     268function rc_sm_api_key_save($slug, $key) {
     269    $saved = rc_sm_tb_settings_get_value('api_keys');
     270    if (empty($saved)) $saved = array();
     271
     272    $saved[$slug] = array(
     273        'key'         => $key,
     274        'valid'       => true,
     275        'verified_at' => current_time('mysql'),
     276    );
     277
     278    rc_sm_api_key_db_save($saved);
     279}
     280
     281function rc_sm_api_key_delete($slug) {
     282    $saved = rc_sm_tb_settings_get_value('api_keys');
     283    if (empty($saved)) return;
     284    unset($saved[$slug]);
     285    rc_sm_api_key_db_save($saved);
     286}
     287
     288function rc_sm_api_key_db_save($keys) {
     289    global $wpdb;
     290    $table = esc_sql(RC_SM_TABLE_SETTINGS);
     291
     292    $exists = $wpdb->get_var($wpdb->prepare(
     293        "SELECT COUNT(*) FROM {$table} WHERE action = %s", // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
     294        'api_keys'
     295    ));
     296
     297    if ($exists) {
     298        $wpdb->update(
     299            $table,
     300            array('value' => wp_json_encode($keys), 'date' => current_time('mysql')),
     301            array('action' => 'api_keys'),
     302            array('%s', '%s'),
     303            array('%s')
     304        );
     305    } else {
     306        $wpdb->insert(
     307            $table,
     308            array('action' => 'api_keys', 'value' => wp_json_encode($keys), 'date' => current_time('mysql')),
     309            array('%s', '%s', '%s')
     310        );
     311    }
     312}
     313
     314
     315// ─── AJAX: verifica licenza salvata da DB (auto-check, nessuna chiave in HTML) ─
     316
     317add_action( 'wp_ajax_rc_sm_premium_verify_saved_licence', function () {
     318
     319    if ( ! rc_sm_security_ajax_verify() ) return;
     320
     321    $slug = rc_sm_security_ajax_get_param( 'slug', '' );
     322
     323    if ( empty( $slug ) ) {
     324        wp_send_json_error( array( 'message' => __( 'Missing required parameters.', 'rc-site-manager-optimization' ) ) );
     325        return;
     326    }
     327
     328    $saved_licences = rc_sm_tb_settings_get_value( 'premium_licences' );
     329    $saved_key      = isset( $saved_licences[ $slug ]['key'] ) ? $saved_licences[ $slug ]['key'] : '';
     330
     331    if ( empty( $saved_key ) ) {
     332        wp_send_json_error( array( 'message' => __( 'No licence key saved.', 'rc-site-manager-optimization' ) ) );
     333        return;
     334    }
     335
     336    $result = rc_sm_premium_licence_verify( $slug, $saved_key );
     337
     338    if ( $result['valid'] ) {
     339        wp_send_json_success( array( 'message' => $result['message'] ) );
     340    } else {
     341        wp_send_json_error( array( 'message' => $result['message'] ) );
     342    }
     343
     344    wp_die();
     345} );
     346
     347
     348// ─── AJAX: verifica API key salvata da DB (auto-check, nessuna chiave in HTML) ─
     349
     350add_action( 'wp_ajax_rc_sm_api_key_verify_saved', function () {
     351
     352    if ( ! rc_sm_security_ajax_verify() ) return;
     353
     354    $slug = rc_sm_security_ajax_get_param( 'slug', '' );
     355
     356    if ( empty( $slug ) ) {
     357        wp_send_json_error( array( 'message' => __( 'Missing required parameters.', 'rc-site-manager-optimization' ) ) );
     358        return;
     359    }
     360
     361    $saved_keys = rc_sm_tb_settings_get_value( 'api_keys' );
     362    $saved_key  = isset( $saved_keys[ $slug ]['key'] ) ? $saved_keys[ $slug ]['key'] : '';
     363
     364    if ( empty( $saved_key ) ) {
     365        wp_send_json_error( array( 'message' => __( 'No API key saved.', 'rc-site-manager-optimization' ) ) );
     366        return;
     367    }
     368
     369    // Riusa la stessa logica di verifica
     370    $response = wp_remote_get(
     371        add_query_arg( array(
     372            'url'      => home_url( '/' ),
     373            'key'      => $saved_key,
     374            'strategy' => 'mobile',
     375            'category' => 'performance',
     376        ), 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed' ),
     377        array( 'timeout' => 20 )
     378    );
     379
     380    if ( is_wp_error( $response ) ) {
     381        wp_send_json_error( array( 'message' => __( 'Connection error. Please try again.', 'rc-site-manager-optimization' ) ) );
     382        return;
     383    }
     384
     385    $body      = json_decode( wp_remote_retrieve_body( $response ), true );
     386    $http_code = (int) wp_remote_retrieve_response_code( $response );
     387
     388    if ( $http_code === 200 && isset( $body['lighthouseResult'] ) ) {
     389        wp_send_json_success( array( 'message' => __( 'API Key valid and active.', 'rc-site-manager-optimization' ) ) );
     390    } else {
     391        $error_msg = isset( $body['error']['message'] )
     392            ? $body['error']['message']
     393            : __( 'Invalid API Key.', 'rc-site-manager-optimization' );
     394        wp_send_json_error( array( 'message' => $error_msg ) );
     395    }
     396
     397    wp_die();
     398} );
  • rc-site-manager-optimization/trunk/settings/settings_script.js

    r3438010 r3487409  
    110110        // Rimuove listener precedente
    111111        $(document).off('click.premium_verify', '.rc_sm_licence_verify_btn');
    112 
    113         // Auto-check licenze al caricamento (silenzioso)
    114         $('.rc_sm_licence_item[data-has-key="1"]').each(function() {
     112        $(document).off('input.licence_unmask', '.rc_sm_licence_input');
     113
     114        // Reset data-is-masked quando l'utente digita una nuova chiave
     115        $(document).on('input.licence_unmask', '.rc_sm_licence_input', function () {
     116            $(this).attr('data-is-masked', '0');
     117        });
     118
     119        // Auto-check al caricamento: server-side, nessuna chiave in JS
     120        $('.rc_sm_licence_item[data-has-key="1"]').each(function () {
    115121            var $item = $(this);
    116             var slug = $item.data('slug');
    117             var $input = $item.find('.rc_sm_licence_input');
    118             var licenceKey = $input.val().trim();
    119            
    120             if (licenceKey) {
    121                 verifyLicence(slug, licenceKey, $item, null, true);
    122             }
    123         });
    124 
    125         // Handle licence verification click
    126         $(document).on('click.premium_verify', '.rc_sm_licence_verify_btn', function(e) {
     122            var slug  = $item.data('slug');
     123            verifyLicenceSaved(slug, $item, null, true);
     124        });
     125
     126        // Click su Verify
     127        $(document).on('click.premium_verify', '.rc_sm_licence_verify_btn', function (e) {
    127128            e.preventDefault();
    128            
    129             var btn = this;
    130             var $btn = $(btn);
    131             var slug = $btn.data('slug');
    132             var $item = $btn.closest('.rc_sm_licence_item');
    133             var $input = $item.find('.rc_sm_licence_input');
    134             var licenceKey = $input.val().trim();
    135            
    136             if (!licenceKey) {
    137                 var $message = $item.find('.rc_sm_licence_message');
    138                 $message.html('<div class="rc_sm_notice" style="margin-bottom: 30px;"><div class="rc_sm_notice_error">Please enter a licence key.</div></div>').show();
    139                 setTimeout(function() {
    140                     $btn.removeClass('rc_sm_loader_active');
    141                     $btn.prop('disabled', false);
    142                     $btn.next('.rc_sm_button_loader_ico').remove();
    143                     $btn.prev('.rc_sm_button_loader_ico').remove();
    144                 }, 100);
    145                 return;
    146             }
    147            
    148             verifyLicence(slug, licenceKey, $item, function() {
    149                 setTimeout(function() {
    150                     $(btn).removeClass('rc_sm_loader_active');
    151                     $(btn).prop('disabled', false);
     129
     130            var btn      = this;
     131            var $btn     = $(btn);
     132            var slug     = $btn.data('slug');
     133            var $item    = $btn.closest('.rc_sm_licence_item');
     134            var $input   = $item.find('.rc_sm_licence_input');
     135            var isMasked = $input.attr('data-is-masked') === '1';
     136
     137            var resetBtn = function () {
     138                setTimeout(function () {
     139                    $(btn).removeClass('rc_sm_loader_active').prop('disabled', false);
    152140                    $(btn).next('.rc_sm_button_loader_ico').remove();
    153141                    $(btn).prev('.rc_sm_button_loader_ico').remove();
    154142                }, 100);
    155             }, false);
    156         });
    157 
    158         // Funzione verifica licenza (silent = true nasconde messaggi)
     143            };
     144
     145            if (isMasked) {
     146                // Chiave mascherata: verifica server-side senza inviarla
     147                verifyLicenceSaved(slug, $item, resetBtn, false);
     148                return;
     149            }
     150
     151            var licenceKey = $input.val().trim();
     152            if (!licenceKey) {
     153                $item.find('.rc_sm_licence_message')
     154                    .html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Please enter a licence key.</div></div>').show();
     155                resetBtn();
     156                return;
     157            }
     158
     159            verifyLicence(slug, licenceKey, $item, resetBtn, false);
     160        });
     161
     162        // Verifica licenza salvata nel DB (server-side, nessuna chiave in JS)
     163        function verifyLicenceSaved(slug, $item, callback, silent) {
     164            var $message = $item.find('.rc_sm_licence_message');
     165            var $status  = $item.find('.rc_sm_licence_status');
     166
     167            if (!silent) { $message.hide(); }
     168
     169            $.ajax({
     170                url:  rc_sm_ajax.ajax_url,
     171                type: 'POST',
     172                data: { action: 'rc_sm_premium_verify_saved_licence', nonce: rc_sm_ajax.nonce, slug: slug }
     173            }).done(function (response) {
     174                if (response && response.success) {
     175                    $status.html('<span class="rc_sm_color_success"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Active</span>');
     176                    if (!silent) {
     177                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_success">' + (response.data.message || 'Licence valid and active.') + '</div></div>').show();
     178                        rc_sm_js_notice_fly_show(response.data.message || 'Licence valid and active.', 'success');
     179                    }
     180                } else {
     181                    var errMsg = (response && response.data && response.data.message) ? response.data.message : 'Invalid licence key.';
     182                    $status.html('<span class="rc_sm_color_error"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Not activated</span>');
     183                    if (!silent) {
     184                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">' + errMsg + '</div></div>').show();
     185                        rc_sm_js_notice_fly_show(errMsg, 'error');
     186                    }
     187                }
     188                if (typeof callback === 'function') callback();
     189            }).fail(function () {
     190                if (!silent) {
     191                    $item.find('.rc_sm_licence_message').html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Connection error. Please try again.</div></div>').show();
     192                    rc_sm_js_notice_fly_show('Connection error. Please try again.', 'error');
     193                }
     194                if (typeof callback === 'function') callback();
     195            });
     196        }
     197
     198        // Verifica nuova chiave licenza (inviata dal client)
    159199        function verifyLicence(slug, licenceKey, $item, callback, silent) {
    160200            var $message = $item.find('.rc_sm_licence_message');
    161             var $status = $item.find('.rc_sm_licence_status');
    162            
    163             if (!silent) {
    164                 $message.hide();
    165             }
    166            
     201            var $status  = $item.find('.rc_sm_licence_status');
     202
     203            if (!silent) { $message.hide(); }
     204
    167205            $.ajax({
    168                 url: rc_sm_ajax.ajax_url,
     206                url:  rc_sm_ajax.ajax_url,
    169207                type: 'POST',
    170208                data: {
    171                     action: 'rc_sm_premium_verify_licence',
    172                     nonce: rc_sm_ajax.nonce,
    173                     slug: slug,
     209                    action:      'rc_sm_premium_verify_licence',
     210                    nonce:       rc_sm_ajax.nonce,
     211                    slug:        slug,
    174212                    licence_key: licenceKey
    175213                }
    176             }).done(function(response) {
     214            }).done(function (response) {
    177215                if (response && response.success) {
    178216                    $status.html('<span class="rc_sm_color_success"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Active</span>');
    179217                    if (!silent) {
    180                         $message.html('<div class="rc_sm_notice" style="margin-bottom: 30px;"><div class="rc_sm_notice_success">' + (response.data.message || 'Licence valid and active') + '</div></div>').show();
     218                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_success">' + (response.data.message || 'Licence valid and active.') + '</div></div>').show();
    181219                        rc_sm_js_notice_fly_show(response.data.message || 'Licence activated successfully.', 'success');
    182220                    }
    183221                } else {
    184                     var errorMsg = (response && response.data && response.data.message) ? response.data.message : 'Invalid licence key.';
     222                    var errMsg = (response && response.data && response.data.message) ? response.data.message : 'Invalid licence key.';
    185223                    $status.html('<span class="rc_sm_color_error"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Not activated</span>');
    186224                    if (!silent) {
    187                         $message.html('<div class="rc_sm_notice" style="margin-bottom: 30px;"><div class="rc_sm_notice_error">' + errorMsg + '</div></div>').show();
    188                         rc_sm_js_notice_fly_show(errorMsg, 'error');
    189                     }
    190                 }
    191                 if (typeof callback === 'function') callback();
    192             }).fail(function() {
     225                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">' + errMsg + '</div></div>').show();
     226                        rc_sm_js_notice_fly_show(errMsg, 'error');
     227                    }
     228                }
     229                if (typeof callback === 'function') callback();
     230            }).fail(function () {
    193231                if (!silent) {
    194                     $message.html('<div class="rc_sm_notice" style="margin-bottom: 30px;"><div class="rc_sm_notice_error">Connection error. Please try again.</div></div>').show();
     232                    $item.find('.rc_sm_licence_message').html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Connection error. Please try again.</div></div>').show();
    195233                    rc_sm_js_notice_fly_show('Connection error. Please try again.', 'error');
    196234                }
     
    202240
    203241});
     242
     243jQuery(function ($) {
     244    $(document).on('rc_sm_tab_loaded', function (e, tabId) {
     245        if (tabId !== 'tab_settings_premium') return;
     246
     247        // Rimuove listener precedente
     248        $(document).off('click.api_key_verify', '.rc_sm_api_key_verify_btn');
     249        $(document).off('input.api_key_unmask', '.rc_sm_api_key_input');
     250
     251        // Reset data-is-masked quando l'utente digita una nuova chiave
     252        $(document).on('input.api_key_unmask', '.rc_sm_api_key_input', function () {
     253            $(this).attr('data-is-masked', '0');
     254        });
     255
     256        // Auto-check al caricamento: server-side, nessuna chiave in JS
     257        $('.rc_sm_api_key_item[data-has-key="1"]').each(function () {
     258            var $item = $(this);
     259            var slug  = $item.data('slug');
     260            verifyApiKeySaved(slug, $item, null, true);
     261        });
     262
     263        // Click su Verify
     264        $(document).on('click.api_key_verify', '.rc_sm_api_key_verify_btn', function (e) {
     265            e.preventDefault();
     266
     267            var btn      = this;
     268            var $btn     = $(btn);
     269            var slug     = $btn.data('slug');
     270            var $item    = $btn.closest('.rc_sm_api_key_item');
     271            var $input   = $item.find('.rc_sm_api_key_input');
     272            var isMasked = $input.attr('data-is-masked') === '1';
     273
     274            var resetBtn = function () {
     275                setTimeout(function () {
     276                    $(btn).removeClass('rc_sm_loader_active').prop('disabled', false);
     277                    $(btn).next('.rc_sm_button_loader_ico').remove();
     278                    $(btn).prev('.rc_sm_button_loader_ico').remove();
     279                }, 100);
     280            };
     281
     282            if (isMasked) {
     283                verifyApiKeySaved(slug, $item, resetBtn, false);
     284                return;
     285            }
     286
     287            var key = $input.val().trim();
     288            if (!key) {
     289                $item.find('.rc_sm_api_key_message')
     290                    .html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Please enter an API key.</div></div>').show();
     291                resetBtn();
     292                return;
     293            }
     294
     295            verifyApiKey(slug, key, $item, resetBtn, false);
     296        });
     297
     298        // Verifica API key salvata nel DB (server-side, nessuna chiave in JS)
     299        function verifyApiKeySaved(slug, $item, callback, silent) {
     300            var $message = $item.find('.rc_sm_api_key_message');
     301            var $status  = $item.find('.rc_sm_api_key_status');
     302
     303            if (!silent) { $message.hide(); }
     304
     305            $.ajax({
     306                url:  rc_sm_ajax.ajax_url,
     307                type: 'POST',
     308                data: { action: 'rc_sm_api_key_verify_saved', nonce: rc_sm_ajax.nonce, slug: slug }
     309            }).done(function (response) {
     310                if (response && response.success) {
     311                    $status.html('<span class="rc_sm_color_success"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Active</span>');
     312                    if (!silent) {
     313                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_success">' + (response.data.message || 'API Key valid and active.') + '</div></div>').show();
     314                        rc_sm_js_notice_fly_show(response.data.message || 'API Key valid and active.', 'success');
     315                    }
     316                } else {
     317                    var errMsg = (response && response.data && response.data.message) ? response.data.message : 'Invalid API Key.';
     318                    $status.html('<span class="rc_sm_color_error"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Not activated</span>');
     319                    if (!silent) {
     320                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">' + errMsg + '</div></div>').show();
     321                        rc_sm_js_notice_fly_show(errMsg, 'error');
     322                    }
     323                }
     324                if (typeof callback === 'function') callback();
     325            }).fail(function () {
     326                if (!silent) {
     327                    $item.find('.rc_sm_api_key_message').html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Connection error. Please try again.</div></div>').show();
     328                    rc_sm_js_notice_fly_show('Connection error. Please try again.', 'error');
     329                }
     330                if (typeof callback === 'function') callback();
     331            });
     332        }
     333
     334        // Verifica nuova API key (inviata dal client)
     335        function verifyApiKey(slug, key, $item, callback, silent) {
     336            var $message = $item.find('.rc_sm_api_key_message');
     337            var $status  = $item.find('.rc_sm_api_key_status');
     338
     339            if (!silent) { $message.hide(); }
     340
     341            $.ajax({
     342                url:  rc_sm_ajax.ajax_url,
     343                type: 'POST',
     344                data: {
     345                    action:  'rc_sm_api_key_verify',
     346                    nonce:   rc_sm_ajax.nonce,
     347                    slug:    slug,
     348                    api_key: key
     349                }
     350            }).done(function (response) {
     351                if (response && response.success) {
     352                    $status.html('<span class="rc_sm_color_success"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Active</span>');
     353                    if (!silent) {
     354                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_success">' + (response.data.message || 'API Key valid and active.') + '</div></div>').show();
     355                        rc_sm_js_notice_fly_show(response.data.message || 'API Key activated successfully.', 'success');
     356                    }
     357                } else {
     358                    var errMsg = (response && response.data && response.data.message) ? response.data.message : 'Invalid API Key.';
     359                    $status.html('<span class="rc_sm_color_error"><span class="dashicons dashicons-marker" style="line-height:inherit;"></span> Not activated</span>');
     360                    if (!silent) {
     361                        $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">' + errMsg + '</div></div>').show();
     362                        rc_sm_js_notice_fly_show(errMsg, 'error');
     363                    }
     364                }
     365                if (typeof callback === 'function') callback();
     366            }).fail(function () {
     367                if (!silent) {
     368                    $message.html('<div class="rc_sm_notice" style="margin-bottom:30px;"><div class="rc_sm_notice_error">Connection error. Please try again.</div></div>').show();
     369                    rc_sm_js_notice_fly_show('Connection error. Please try again.', 'error');
     370                }
     371                if (typeof callback === 'function') callback();
     372            });
     373        }
     374
     375    });
     376});
  • rc-site-manager-optimization/trunk/settings/tab_premium.php

    r3438010 r3487409  
    2121        </div>
    2222
     23    </div><div class="rc_sm_bs_col_6">
     24
     25        <div class="rc_sm_box_wrap rc_sm_box_form">
     26
     27            <h3><?php echo esc_html__('API Keys', 'rc-site-manager-optimization'); ?></h3>
     28
     29            <p><?php echo esc_html__('Enter your API keys to enable external integrations.', 'rc-site-manager-optimization'); ?></p>
     30
     31            <div class="rc_sm_api_keys_wrap">
     32                <?php
     33                    echo rc_sm_api_keys_form(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Function handles escaping internally
     34                ?>
     35            </div>
     36
     37        </div>
     38
    2339    </div>
    2440
  • rc-site-manager-optimization/trunk/settings/tab_premium_fn.php

    r3472200 r3487409  
    22if ( ! defined( 'ABSPATH' ) ) exit;
    33
     4/**
     5 * Maschera una chiave mostrando i primi 10 e gli ultimi 5 caratteri.
     6 * La chiave completa non viene mai emessa nell'HTML.
     7 */
     8function rc_sm_mask_key( $key ) {
     9    $len = strlen( $key );
     10    if ( $len <= 15 ) {
     11        return str_repeat( '*', $len );
     12    }
     13    return substr( $key, 0, 10 ) . str_repeat( '*', $len - 15 ) . substr( $key, -5 );
     14}
     15
    416function rc_sm_premium_form() {
    5    
    6     // Definizione servizi premium
     17
    718    $services = array(
    819        'sus' => array(
    9             'name' => 'RC Site Ultra Speed',
     20            'name'   => 'RC Site Ultra Speed',
    1021            'prefix' => 'rc_sus_'
    1122        ),
    1223        'cs1' => array(
    13             'name' => 'RC Custom Software 1',
     24            'name'   => 'RC Custom Software 1',
    1425            'prefix' => 'rc_cs1_'
    1526        )
    1627    );
    17    
    18     // Recupera licenze salvate dal database
    19     $saved_licences = rc_sm_tb_settings_get_value('premium_licences');
    20     if (empty($saved_licences)) {
     28
     29    $saved_licences = rc_sm_tb_settings_get_value( 'premium_licences' );
     30    if ( empty( $saved_licences ) ) {
    2131        $saved_licences = array();
    2232    }
    23    
     33
    2434    $html = '';
    25    
    26     foreach ($services as $slug => $service) {
    27        
     35
     36    foreach ( $services as $slug => $service ) {
     37
    2838        $field_name = 'licence_' . $slug;
    29         $saved_key = isset($saved_licences[$slug]['key']) ? $saved_licences[$slug]['key'] : '';
    30        
    31         $html .= '<div class="rc_sm_form_divider rc_sm_licence_item" data-slug="' . esc_attr($slug) . '" data-has-key="' . (!empty($saved_key) ? '1' : '0') . '">';
    32        
    33         // Titolo servizio con stato di fianco
     39        $saved_key  = isset( $saved_licences[ $slug ]['key'] ) ? $saved_licences[ $slug ]['key'] : '';
     40        $has_key    = ! empty( $saved_key ) ? '1' : '0';
     41        $display    = ! empty( $saved_key ) ? rc_sm_mask_key( $saved_key ) : '';
     42
     43        $html .= '<div class="rc_sm_form_divider rc_sm_licence_item" data-slug="' . esc_attr( $slug ) . '" data-has-key="' . $has_key . '">';
     44
    3445        $html .= '<div class="rc_sm_bs_row">';
    3546        $html .= '<div class="rc_sm_bs_col_6">';
    36         $html .= '<h4 style="margin-top:0;">' . esc_html($service['name']) . '</h4>';
     47        $html .= '<h4 style="margin-top:0;">' . esc_html( $service['name'] ) . '</h4>';
    3748        $html .= '</div>';
    3849        $html .= '<div class="rc_sm_bs_col_6 rc_sm_bs_col_align">';
    3950        $html .= '<div class="rc_sm_licence_status" style="margin-top:0;">';
    40         $html .= '<span class="rc_sm_color_warning"><span class="dashicons dashicons-update" style="line-height:inherit;"></span> ' . esc_html__('Checking', 'rc-site-manager-optimization') . '...</span>';
     51        $html .= '<span class="rc_sm_color_warning"><span class="dashicons dashicons-update" style="line-height:inherit;"></span> ' . esc_html__( 'Checking', 'rc-site-manager-optimization' ) . '...</span>';
    4152        $html .= '</div>';
    4253        $html .= '</div>';
    4354        $html .= '</div>';
    44        
    45         // Area messaggio (sempre nascosta inizialmente)
     55
    4656        $html .= '<div class="rc_sm_licence_message" style="display:none;"></div>';
    47        
    48         // Campo input licenza con bottone verifica
     57
    4958        $html .= '<div class="rc_sm_licence_input_wrap">';
    5059        $html .= '<div class="rc_sm_bs_row">';
    5160        $html .= '<div class="rc_sm_bs_col_10">';
    52         $html .= '<input type="text" name="' . esc_attr($field_name) . '" class="rc_sm_licence_input" placeholder="' . esc_attr($service['prefix']) . 'xxxxxxxxxx..." value="' . esc_attr($saved_key) . '">';
    53         $html .= '<div class="rc_sm_form_help rc_sm_color_light">' . esc_html__('Prefix', 'rc-site-manager-optimization') . ' "' . esc_attr($service['prefix']) . '" ' . esc_html__('+ "50 alphanumeric characters"', 'rc-site-manager-optimization') . '</div>';
     61        $html .= '<input type="text" name="' . esc_attr( $field_name ) . '" class="rc_sm_licence_input" placeholder="' . esc_attr( $service['prefix'] ) . 'xxxxxxxxxx..." value="' . esc_attr( $display ) . '" data-is-masked="' . $has_key . '" autocomplete="off">';
     62        $html .= '<div class="rc_sm_form_help rc_sm_color_light">' . esc_html__( 'Prefix', 'rc-site-manager-optimization' ) . ' "' . esc_attr( $service['prefix'] ) . '" ' . esc_html__( '+ "50 alphanumeric characters"', 'rc-site-manager-optimization' ) . '</div>';
    5463        $html .= '</div>';
    5564        $html .= '<div class="rc_sm_bs_col_2 rc_sm_bs_col_align_right">';
    56         $html .= '<button type="button" class="rc_sm_button rc_sm_bg_success rc_sm_size_lg rc_sm_button_loader rc_sm_button_loader_left rc_sm_licence_verify_btn" data-slug="' . esc_attr($slug) . '">' . esc_html__('Verify', 'rc-site-manager-optimization') . '</button>';
     65        $html .= '<button type="button" class="rc_sm_button rc_sm_bg_success rc_sm_size_lg rc_sm_button_loader rc_sm_button_loader_left rc_sm_licence_verify_btn" data-slug="' . esc_attr( $slug ) . '">' . esc_html__( 'Verify', 'rc-site-manager-optimization' ) . '</button>';
    5766        $html .= '</div>';
    5867        $html .= '</div>';
    5968        $html .= '</div>';
    60        
     69
    6170        $html .= '</div>';
    6271    }
    63    
     72
    6473    return $html;
    6574}
     75
     76function rc_sm_api_keys_form() {
     77
     78    $api_keys_config = array(
     79        'google_cloud' => array(
     80            'name'        => 'Google Cloud API Key',
     81            'placeholder' => 'AIzaSy...',
     82            'help'        => __( 'Used for: PageSpeed Insights, Google Maps, YouTube Data API, Google Translate, Google Vision, Google Search Console API, Google Analytics Data API', 'rc-site-manager-optimization' ),
     83        )
     84    );
     85
     86    $saved_keys = rc_sm_tb_settings_get_value( 'api_keys' );
     87    if ( empty( $saved_keys ) ) {
     88        $saved_keys = array();
     89    }
     90
     91    $html = '';
     92
     93    foreach ( $api_keys_config as $slug => $config ) {
     94
     95        $saved_key = isset( $saved_keys[ $slug ]['key'] ) ? $saved_keys[ $slug ]['key'] : '';
     96        $has_key   = ! empty( $saved_key ) ? '1' : '0';
     97        $display   = ! empty( $saved_key ) ? rc_sm_mask_key( $saved_key ) : '';
     98
     99        $html .= '<div class="rc_sm_form_divider rc_sm_api_key_item" data-slug="' . esc_attr( $slug ) . '" data-has-key="' . $has_key . '">';
     100
     101        $html .= '<div class="rc_sm_bs_row">';
     102        $html .= '<div class="rc_sm_bs_col_6"><h4 style="margin-top:0;">' . esc_html( $config['name'] ) . '</h4></div>';
     103        $html .= '<div class="rc_sm_bs_col_6 rc_sm_bs_col_align">';
     104        $html .= '<div class="rc_sm_api_key_status" style="margin-top:0;">';
     105        $html .= '<span class="rc_sm_color_warning"><span class="dashicons dashicons-update" style="line-height:inherit;"></span> ' . esc_html__( 'Checking', 'rc-site-manager-optimization' ) . '...</span>';
     106        $html .= '</div>';
     107        $html .= '</div>';
     108        $html .= '</div>';
     109
     110        $html .= '<div class="rc_sm_api_key_message" style="display:none;"></div>';
     111
     112        $html .= '<div class="rc_sm_api_key_input_wrap">';
     113        $html .= '<div class="rc_sm_bs_row">';
     114        $html .= '<div class="rc_sm_bs_col_10">';
     115        $html .= '<input type="text" name="api_key_' . esc_attr( $slug ) . '" class="rc_sm_api_key_input" placeholder="' . esc_attr( $config['placeholder'] ) . '" value="' . esc_attr( $display ) . '" data-is-masked="' . $has_key . '" autocomplete="off">';
     116        $html .= '<div class="rc_sm_form_help rc_sm_color_light">' . esc_html( $config['help'] ) . '</div>';
     117        $html .= '</div>';
     118        $html .= '<div class="rc_sm_bs_col_2 rc_sm_bs_col_align_right">';
     119        $html .= '<button type="button" class="rc_sm_button rc_sm_bg_success rc_sm_size_lg rc_sm_button_loader rc_sm_button_loader_left rc_sm_api_key_verify_btn" data-slug="' . esc_attr( $slug ) . '">' . esc_html__( 'Verify', 'rc-site-manager-optimization' ) . '</button>';
     120        $html .= '</div>';
     121        $html .= '</div>';
     122        $html .= '</div>';
     123
     124        $html .= '</div>';
     125    }
     126
     127    return $html;
     128}
  • rc-site-manager-optimization/trunk/utility/tab_log.php

    r3480548 r3487409  
    2828        $type = esc_attr($key);
    2929        $file_key = esc_attr($log['file_key']);
    30         $file_exists = file_exists($path) && strpos($path, '/dev/') === false;
    3130
    32         $size = file_exists($path) ? filesize($path) : 0;
     31        $is_same_as_debug = $key === 'php_errorlog' && ( empty( $path ) || strpos( $path, 'debug.log' ) !== false );
     32        $file_exists = !$is_same_as_debug && file_exists($path) && strpos($path, '/dev/') === false;
     33
     34        $size = $file_exists ? filesize($path) : 0;
    3335        $size_mb = $size / (1024 * 1024);
    3436?>
    3537    <div class="rc_sm_bs_col_6">
    3638        <div class="rc_sm_box_wrap">
    37             <h3><?php echo esc_html($label); ?>
    38                 <?php if ($file_exists): ?>
    39                     - <?php echo wp_kses(rc_sm_file_size_html($size), [
    40                         'span' => [
    41                             'class' => [],
    42                         ],
    43                     ]); ?>
    44                 <?php endif; ?>
    45                 <?php echo wp_kses_post(rc_sm_help_link('utility_log', '?')); ?>
    46             </h3>
     39            <div class="rc_sm_bs_row rc_sm_title_filter">
     40                <div class="rc_sm_bs_col_6">
     41                    <h3 style="padding-top: 0px;"><?php echo esc_html($label); ?>
     42                        <?php if ($file_exists): ?>
     43                            - <?php echo wp_kses(rc_sm_file_size_html($size), ['span' => ['class' => []]]); ?>
     44                        <?php endif; ?>
     45                        <?php echo wp_kses_post(rc_sm_help_link('utility_log', '?')); ?>
     46                    </h3>
     47                </div>
     48                <?php if ($file_exists && $size_mb < 5): ?>
     49                <div class="rc_sm_bs_col_6 rc_sm_bs_col_align">
     50                    <div class="rc_sm_bs_col_align_right">
     51                        <a href="#" class="rc_sm_tooltip_copy rc_sm_tooltip_sx rc_sm_button rc_sm_bg_primary" data-tooltip-copy-class="<?php echo esc_attr($id); ?>">
     52                            <?php echo esc_html__( 'Copy Code', 'rc-site-manager-optimization' ); ?>
     53                        </a>
     54                    </div>
     55                </div>
     56                <?php endif; ?>
     57            </div>
    4758
    48             <?php if ($file_exists): ?>
     59            <?php if ($is_same_as_debug): ?>
     60                <p><?php echo esc_html__( 'Same as debug.log', 'rc-site-manager-optimization' ); ?></p>
     61            <?php elseif ($file_exists): ?>
    4962                <?php if ($size_mb < 5): ?>
    5063                    <textarea class="<?php echo esc_attr($id); ?>" readonly rows="20" style="width:100%; background:#fff; border:1px solid #ccc; padding:10px;"><?php echo esc_textarea(file_get_contents($path)); ?></textarea>
  • rc-site-manager-optimization/trunk/utility/tab_system_fn.php

    r3487089 r3487409  
    176176    $data = [];
    177177    foreach ( $files as $label => $path ) {
     178        if ( $label === 'php_errorlog' && ( empty( $path ) || strpos( $path, 'debug.log' ) !== false ) ) {
     179            $data[ $label ] = [ 'value' => 'N/A', 'source' => esc_html__( 'Same as debug.log', 'rc-site-manager-optimization' ) ];
     180            continue;
     181        }
    178182        if ( $path && file_exists( $path ) ) {
    179183            $size     = filesize( $path );
Note: See TracChangeset for help on using the changeset viewer.