close

Plugin Directory

Changeset 3332295


Ignore:
Timestamp:
07/22/2025 01:40:25 PM (9 months ago)
Author:
dueclic
Message:

Update to version 3.5.0 from GitHub

Location:
two-factor-login-telegram
Files:
18 added
2 deleted
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • two-factor-login-telegram/tags/3.5.0/README.md

    r3331421 r3332295  
    55Requires PHP: 7.0
    66Tested up to: 6.8
    7 Stable tag: 3.4
     7Stable tag: 3.5.0
    88License: GPLv3
    99License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    5252
    5353== Changelog ==
     54
     55= 3.5.0 =
     56* **Enhanced Logs System**: Replaced simple logs with professional WP_List_Table implementation featuring pagination (10 items per page), sorting, and bulk actions
     57* **Improved User Interface**: Complete UI overhaul with enhanced styling, better form layouts, and improved user experience
     58* **Advanced Database Management**: Migrated to MySQL tables for better performance and reliability instead of WordPress options
     59* **Better Chat ID Validation**: Enhanced Chat ID validation with proper format checking for both user and group chats
     60* **JavaScript Translations**: Implemented proper internationalization for all JavaScript messages using wp_localize_script
     61* **Enhanced User Feedback**: Added contextual status messages during 2FA configuration with clear visual indicators
     62* **Template System**: Introduced dedicated error templates for better error handling and user guidance
     63* **Timestamp Formatting**: Logs now respect WordPress date/time format settings for consistent display
     64* **Bug Fixes**: Fixed duplicate Chat ID input elements issue and improved form validation
     65* **Performance Improvements**: Optimized database queries and reduced memory usage
    5466
    5567= 3.4 =
  • two-factor-login-telegram/tags/3.5.0/assets/css/wp-factor-telegram-plugin.css

    r2805641 r3332295  
    137137
    138138#wpft-howto .ui-accordion-header {
     139    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     140}
     141
     142/* 2FA Configuration Improvements */
     143#tg-2fa-configuration {
     144    opacity: 0;
     145    max-height: 0;
     146    overflow: hidden;
     147    transition: all 0.3s ease-in-out;
     148    transform: translateY(-10px);
     149}
     150
     151#tg-2fa-configuration.show {
     152    opacity: 1;
     153    max-height: 1000px;
     154    transform: translateY(0);
     155}
     156
     157#tg-2fa-configuration .form-table {
     158    background: #f9f9f9;
     159    border-radius: 8px;
     160    padding: 20px;
     161    margin-top: 15px;
     162    border: 1px solid #e1e1e1;
     163}
     164
     165/* Step indicator */
     166.tg-setup-steps {
     167    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     168    color: white;
     169    padding: 20px;
     170    border-radius: 8px;
     171    margin-bottom: 20px;
     172}
     173
     174.tg-setup-steps ol {
     175    margin: 0;
     176    padding-left: 20px;
     177}
     178
     179.tg-setup-steps li {
     180    margin-bottom: 10px;
     181    line-height: 1.5;
     182}
     183
     184.tg-setup-steps a {
     185    color: #fff;
     186    text-decoration: underline;
     187}
     188
     189.tg-setup-steps strong {
     190    font-weight: 600;
     191}
     192
     193/* Input improvements */
     194#tg_wp_factor_chat_id {
     195    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
     196    font-size: 14px;
     197    padding: 8px 12px;
     198    border: 2px solid #ddd;
     199    border-radius: 6px;
     200    transition: all 0.2s ease;
     201}
     202
     203#tg_wp_factor_chat_id:focus {
     204    border-color: #667eea;
     205    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
     206    outline: none;
     207}
     208
     209#tg_wp_factor_chat_id.input-valid {
     210    border-color: #46b450 !important;
     211    box-shadow: 0 0 0 2px rgba(70, 180, 80, 0.2) !important;
     212}
     213
     214/* Button improvements */
     215.tg-action-button {
     216    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     217    border: none;
     218    color: white;
     219    padding: 10px 20px;
     220    border-radius: 6px;
     221    cursor: pointer;
     222    font-weight: 500;
     223    transition: all 0.2s ease;
     224    position: relative;
     225    overflow: hidden;
     226}
     227
     228.tg-action-button:hover {
     229    transform: translateY(-1px);
     230    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
     231}
     232
     233.tg-action-button:active {
     234    transform: translateY(0);
     235}
     236
     237.tg-action-button.disabled {
     238    opacity: 0.6;
     239    cursor: not-allowed;
     240    transform: none !important;
     241    box-shadow: none !important;
     242}
     243
     244/* Loading spinner improvements */
     245.load-spinner {
     246    display: inline-block;
     247    margin-left: 10px;
     248    vertical-align: middle;
     249}
     250
     251/* FAQ Section Styling */
     252#wpft-howto {
     253    margin: 20px 0;
     254}
     255
     256#wpft-howto h3 {
    139257    background-color: #389abe;
    140 }
     258    color: white;
     259    padding: 15px 20px;
     260    margin: 20px 0 0 0;
     261    font-size: 16px;
     262    border-radius: 5px 5px 0 0;
     263}
     264
     265#wpft-howto .faq-content {
     266    background-color: #f9f9f9;
     267    padding: 20px;
     268    border: 1px solid #ddd;
     269    border-top: none;
     270    border-radius: 0 0 5px 5px;
     271    margin-bottom: 20px;
     272}
     273
     274#wpft-howto p {
     275    margin: 10px 0;
     276    line-height: 1.6;
     277}
     278
     279#wpft-howto ol {
     280    margin: 15px 0;
     281    padding-left: 20px;
     282}
     283
     284#wpft-howto li {
     285    margin-bottom: 10px;
     286    line-height: 1.5;
     287}
     288
     289#wpft-howto .command {
     290    background: #333;
     291    color: #fff;
     292    padding: 2px 6px;
     293    border-radius: 3px;
     294    font-family: monospace;
     295    font-size: 13px;
     296}
     297
     298#wpft-howto .screenshot-container {
     299    margin: 15px 0;
     300    text-align: center;
     301    padding: 10px;
     302    background: #fff;
     303    border: 1px solid #ddd;
     304    border-radius: 3px;
     305}
     306
     307#wpft-howto .help-screenshot {
     308    max-width: 100%;
     309    height: auto;
     310    border: 1px solid #ccc;
     311}
     312
     313#wpft-howto .notice {
     314    padding: 12px;
     315    margin: 15px 0;
     316    border-left: 4px solid #00a0d2;
     317    background: #f7fcfe;
     318}
     319
     320#wpft-howto .external-link {
     321    color: #0073aa;
     322    text-decoration: none;
     323}
     324
     325#wpft-howto .external-link:hover {
     326    text-decoration: underline;
     327}
     328
     329.load-spinner img {
     330    width: 20px;
     331    height: 20px;
     332}
     333
     334/* Progress indicator */
     335.tg-progress {
     336    background: #f0f0f0;
     337    border-radius: 10px;
     338    height: 6px;
     339    overflow: hidden;
     340    margin: 15px 0;
     341}
     342
     343.tg-progress-bar {
     344    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     345    height: 100%;
     346    border-radius: 10px;
     347    transition: width 0.3s ease;
     348    width: 0%;
     349}
     350
     351/* Status indicators */
     352.tg-status {
     353    display: inline-flex;
     354    align-items: center;
     355    padding: 8px 12px;
     356    border-radius: 6px;
     357    font-size: 12px;
     358    font-weight: 500;
     359    margin-top: 10px;
     360}
     361
     362.tg-status.success {
     363    background: #d4edda;
     364    color: #155724;
     365    border: 1px solid #c3e6cb;
     366}
     367
     368.tg-status.error {
     369    background: #f8d7da;
     370    color: #721c24;
     371    border: 1px solid #f5c6cb;
     372}
     373
     374.tg-status.warning {
     375    background: #fff3cd;
     376    color: #856404;
     377    border: 1px solid #ffeeba;
     378}
     379
     380.tg-status::before {
     381    content: '';
     382    width: 8px;
     383    height: 8px;
     384    border-radius: 50%;
     385    margin-right: 8px;
     386}
     387
     388.tg-status.success::before {
     389    background: #28a745;
     390}
     391
     392.tg-status.error::before {
     393    background: #dc3545;
     394}
     395
     396.tg-status.warning::before {
     397    background: #ffc107;
     398}
     399
     400/* Confirmation field improvements */
     401#factor-chat-confirm {
     402    background: #f8f9fa;
     403    border-radius: 8px;
     404    padding: 15px;
     405    border: 1px solid #e9ecef;
     406    margin-top: 15px;
     407}
     408
     409#tg_wp_factor_chat_id_confirm {
     410    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
     411    font-size: 14px;
     412    padding: 8px 12px;
     413    border: 2px solid #ddd;
     414    border-radius: 6px;
     415    transition: all 0.2s ease;
     416}
     417
     418#tg_wp_factor_chat_id_confirm:focus {
     419    border-color: #667eea;
     420    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
     421    outline: none;
     422}
     423
     424/* Responsive improvements */
     425@media (max-width: 768px) {
     426    #tg-2fa-configuration .form-table td {
     427        display: block;
     428        width: 100%;
     429        padding-bottom: 10px;
     430    }
     431
     432    .tg-action-button {
     433        width: 100%;
     434        margin-top: 10px;
     435    }
     436}
  • two-factor-login-telegram/tags/3.5.0/assets/js/wp-factor-telegram-plugin.js

    r3168516 r3332295  
    99    var $twbtn = $("#tg_wp_factor_chat_id_send");
    1010    var $twctrl = $("#tg_wp_factor_valid");
     11    var $twenabled = $("#tg_wp_factor_enabled");
     12    var $twconfig = $("#tg-2fa-configuration");
     13    var $tweditbtn = $("#tg-edit-chat-id");
     14    var $twconfigrow = $(".tg-configured-row");
    1115
    1216    var $twfcr = $("#factor-chat-response");
     
    2327    function init() {
    2428
     29        // Handle checkbox toggle for 2FA configuration with smooth animation
     30        $twenabled.on("change", function(evt){
     31            var isConfigured = $twconfigrow.length > 0;
     32
     33            if ($(this).is(":checked") && !isConfigured) {
     34                $twconfig.addClass('show').show();
     35                updateProgress(25);
     36            } else {
     37                $twconfig.removeClass('show');
     38                setTimeout(function() {
     39                    $twconfig.hide();
     40                }, 300);
     41                $twctrl.val(0);
     42                updateProgress(0);
     43                resetStatusIndicators();
     44            }
     45        });
     46
     47        // Handle edit button click (when 2FA is already configured)
     48        $tweditbtn.on("click", function(evt){
     49            evt.preventDefault();
     50
     51            // Hide configured row and show configuration form
     52            $twconfigrow.hide();
     53            $twconfig.addClass('show').show();
     54
     55            // Make the input editable and clear it
     56            $twfci.prop('readonly', false).removeClass('input-valid').css('background', '').val('');
     57
     58            // Reset validation state
     59            $twctrl.val(0);
     60            updateProgress(25);
     61            resetStatusIndicators();
     62           
     63            // Show modifying status message
     64            $('.tg-status.success').removeClass('success').addClass('warning').text(tlj.modifying_setup);
     65        });
     66
     67        // Watch for changes in Chat ID when in edit mode
     68        $twfci.on("input", function(){
     69            var currentValue = $(this).val();
     70            var originalValue = $(this).data('original-value');
     71
     72            // If value changed from original, require re-validation
     73            if (currentValue !== originalValue) {
     74                $twctrl.val(0);
     75                $(this).removeClass('input-valid');
     76            }
     77        });
     78
     79        // Initialize visibility based on checkbox state and configuration
     80        var isConfigured = $twconfigrow.length > 0;
     81        if ($twenabled.is(":checked") && !isConfigured) {
     82            $twconfig.addClass('show').show();
     83            updateProgress(25);
     84        } else {
     85            $twconfig.removeClass('show').hide();
     86        }
     87
     88        // Store original chat ID value for comparison
     89        if ($twfci.length) {
     90            $twfci.data('original-value', $twfci.val());
     91        }
     92
    2593        $twfci.on("change", function(evt){
    2694           $twctrl.val(0);
     95           // Validate Chat ID format (basic validation)
     96           validateChatId($(this).val());
    2797        });
    2898
    2999        $twbtn.on("click", function(evt){
    30 
    31100            evt.preventDefault();
    32101            var chat_id = $twfci.val();
     102
     103            if (!validateChatId(chat_id)) {
     104                showStatus('#chat-id-status', 'error', tlj.invalid_chat_id);
     105                return;
     106            }
     107
    33108            send_tg_token(chat_id);
    34 
    35109        });
    36110
    37111        $twfcheck.on("click", function(evt){
    38 
    39112            evt.preventDefault();
    40113            var token = $twfciconf.val();
    41114            var chat_id = $twfci.val();
     115
     116            if (!token.trim()) {
     117                showStatus('#validation-status', 'error', tlj.enter_confirmation_code);
     118                return;
     119            }
     120
    42121            check_tg_token(token, chat_id);
    43 
    44122        });
    45123
     
    106184                $twfcheck.addClass('disabled').after('<div class="load-spinner"><img src="'+tlj.spinner+'" /></div>');
    107185                $twfcr.hide();
     186                hideStatus('#validation-status');
    108187            },
    109188            dataType: "json",
     
    114193                    $twfci.addClass("input-valid");
    115194                    $twctrl.val(1);
     195                    updateProgress(100);
     196                    showStatus('#validation-status', 'success', tlj.setup_completed);
    116197                }
    117198                else {
    118                     $twfcr.find(".wpft-notice p").text(response.msg);
    119                     $twfcr.show();
     199                    showStatus('#validation-status', 'error', response.msg);
    120200                    $twfci.removeClass("input-valid");
    121201                    $twctrl.val(0);
     
    124204            },
    125205            error: function(xhr, ajaxOptions, thrownError){
    126                 $twfcr.find(".wpft-notice p").text(tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    127                 $twfcr.show();
     206                showStatus('#validation-status', 'error', tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    128207                $twfci.removeClass("input-valid");
    129208            },
     
    152231                $twfcr.hide();
    153232                $twfconf.hide();
     233                hideStatus('#chat-id-status');
    154234            },
    155235            dataType: "json",
     
    159239                    $twfconf.show();
    160240                    $twfci.removeClass("input-valid");
     241                    updateProgress(75);
     242                    showStatus('#chat-id-status', 'success', tlj.code_sent);
    161243                }
    162244                else {
    163                     $twfcr.find(".wpft-notice p").text(response.msg);
    164                     $twfcr.show();
     245                    showStatus('#chat-id-status', 'error', response.msg);
    165246                }
    166247
    167248            },
    168249            error: function(xhr, ajaxOptions, thrownError){
    169                 $twfcr.find(".wpft-notice p").text(tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    170                 $twfcr.show();
     250                showStatus('#chat-id-status', 'error', tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    171251            },
    172252            complete: function() {
     
    180260    }
    181261
    182 
     262    // Helper functions
     263    function updateProgress(percentage) {
     264        $('#tg-progress-bar').css('width', percentage + '%');
     265    }
     266
     267    function validateChatId(chatId) {
     268        // Telegram Chat ID validation: must be numeric (positive for users, negative for groups)
     269        if (!chatId || typeof chatId !== 'string') {
     270            return false;
     271        }
     272       
     273        var trimmedId = chatId.trim();
     274        if (trimmedId === '') {
     275            return false;
     276        }
     277       
     278        // Check if it's a valid number (can be negative for groups)
     279        var numericId = parseInt(trimmedId, 10);
     280        return !isNaN(numericId) && trimmedId === numericId.toString();
     281    }
     282
     283    function showStatus(selector, type, message) {
     284        var $status = $(selector);
     285        $status.removeClass('success error warning')
     286               .addClass(type)
     287               .text(message)
     288               .fadeIn(300);
     289    }
     290
     291    function hideStatus(selector) {
     292        $(selector).fadeOut(300);
     293    }
     294
     295    function resetStatusIndicators() {
     296        hideStatus('#chat-id-status');
     297        hideStatus('#validation-status');
     298    }
    183299
    184300}(jQuery);
  • two-factor-login-telegram/tags/3.5.0/includes/class-wp-factor-telegram-plugin.php

    r3331421 r3332295  
    5656        require_once(dirname(WP_FACTOR_TG_FILE)
    5757            . "/includes/class-wp-telegram.php");
     58        require_once(dirname(WP_FACTOR_TG_FILE)
     59            . "/includes/class-telegram-logs-list-table.php");
    5860    }
    5961
     
    235237
    236238    /**
    237      * Authentication page
    238      *
    239      * @param $user
    240      */
    241 
    242     private function authentication_page($user)
    243     {
    244         require_once(ABSPATH . '/wp-admin/includes/template.php');
    245         ?>
    246 
    247         <p class="notice notice-warning">
    248             <?php
    249             _e("Enter the code sent to your Telegram account.",
    250                 "two-factor-login-telegram");
    251             ?>
    252         </p>
    253         <p>
    254             <label for="authcode" style="padding-top:1em">
    255                 <?php
    256                 _e("Authentication code:", "two-factor-login-telegram");
    257                 ?>
    258             </label>
    259             <input type="text" name="authcode" id="authcode" class="input"
    260                    value="" size="5"/>
    261         </p>
    262         <?php
    263         submit_button(__('Login with Telegram',
    264             'two-factor-login-telegram'));
    265     }
    266 
    267     /**
    268239     * Login HTML Page
    269240     *
     
    280251        }
    281252
    282         login_header();
    283 
    284         if (!empty($error_msg)) {
    285             echo '<div id="login_error"><strong>' . esc_html($error_msg)
    286                 . '</strong><br /></div>';
    287         }
    288253        // Filter hook to add a custom logo to the 2FA login screen
    289254        $plugin_logo = apply_filters('two_factor_login_telegram_logo',
    290255            plugins_url('assets/img/plugin_logo.png', WP_FACTOR_TG_FILE));
    291         ?>
    292 
    293         <style>
    294             body.login div#login h1 a {
    295                 background-image: url("<?php echo esc_url($plugin_logo); ?>");
    296             }
    297         </style>
    298 
    299         <form name="validate_tg" id="loginform" action="<?php
    300         echo esc_url(site_url('wp-login.php?action=validate_tg',
    301             'login_post')); ?>" method="post" autocomplete="off">
    302             <input type="hidden" name="nonce"
    303                    value="<?php echo wp_create_nonce('wp2fa_telegram_auth_nonce_' . $user->ID); ?>">
    304             <input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php
    305             echo esc_attr($user->ID); ?>"/>
    306             <input type="hidden" name="redirect_to" value="<?php
    307             echo esc_attr($redirect_to); ?>"/>
    308             <input type="hidden" name="rememberme" id="rememberme" value="<?php
    309             echo esc_attr($rememberme); ?>"/>
    310 
    311             <?php
    312             $this->authentication_page($user); ?>
    313         </form>
    314 
    315         <p id="backtoblog">
    316             <a href="<?php
    317             echo esc_url(home_url('/')); ?>" title="<?php
    318             __("Are you lost?", "two-factor-login-telegram"); ?>"><?php
    319                 echo sprintf(__('&larr; Back to %s',
    320                     'two-factor-login-telegram'),
    321                     get_bloginfo('title', 'display')); ?></a>
    322         </p>
    323 
    324         <?php
    325         do_action('login_footer'); ?>
    326         <div class="clear"></div>
    327         </body>
    328 
    329         </html>
    330         <?php
     256
     257        require_once(ABSPATH . '/wp-admin/includes/template.php');
     258        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/login-form.php");
    331259    }
    332260
     
    361289        $hashed_auth_code = hash('sha256', $authcode);
    362290        $current_datetime = current_time('mysql');
    363         $query = $wpdb->prepare(
     291
     292        // Check if token exists for this user
     293        $token_exists_query = $wpdb->prepare(
    364294            "SELECT COUNT(*)
    365         FROM $table_name
    366         WHERE auth_code = %s
    367         AND user_id = %d
    368         AND expiration_date > %s",
     295            FROM $table_name
     296            WHERE auth_code = %s
     297            AND user_id = %d",
     298            $hashed_auth_code, $user_id
     299        );
     300
     301        $token_exists = ($wpdb->get_var($token_exists_query) > 0);
     302
     303        if (!$token_exists) {
     304            return 'invalid'; // Invalid token
     305        }
     306
     307        // Check if token is not expired
     308        $valid_token_query = $wpdb->prepare(
     309            "SELECT COUNT(*)
     310            FROM $table_name
     311            WHERE auth_code = %s
     312            AND user_id = %d
     313            AND expiration_date > %s",
    369314            $hashed_auth_code, $user_id, $current_datetime
    370315        );
    371316
    372         return ($wpdb->get_var($query) > 0);
     317        $is_valid = ($wpdb->get_var($valid_token_query) > 0);
     318
     319        if (!$is_valid) {
     320            return 'expired'; // Token exists but expired
     321        }
     322
     323        return 'valid'; // Valid token
    373324    }
    374325
     
    393344        }
    394345
    395         if (true !== $this->is_valid_authcode($_REQUEST['authcode'], $user->ID)) {
     346        $authcode_validation = $this->is_valid_authcode($_REQUEST['authcode'], $user->ID);
     347
     348        if ('valid' !== $authcode_validation) {
    396349            do_action('wp_factor_telegram_failed', $user->user_login);
    397350
     
    400353            $chat_id = $this->get_user_chatid($user->ID);
    401354            $result = $this->telegram->send_tg_token($auth_code, $chat_id, $user->ID);
     355
     356            // Determine error message based on validation result
     357            $error_message = '';
     358            $log_reason = '';
     359
     360            if ($authcode_validation === 'expired') {
     361                $error_message = __('The verification code has expired. We just sent you a new code, please try again!',
     362                    'two-factor-login-telegram');
     363                $log_reason = 'expired_verification_code';
     364            } else {
     365                $error_message = __('Wrong verification code, we just sent a new code, please try again!',
     366                    'two-factor-login-telegram');
     367                $log_reason = 'wrong_verification_code';
     368            }
    402369
    403370            $this->log_telegram_action('auth_code_resent', array(
     
    406373                'chat_id' => $chat_id,
    407374                'success' => $result !== false,
    408                 'reason' => 'wrong_verification_code'
     375                'reason' => $log_reason
    409376            ));
    410377
    411             $this->login_html($user, $_REQUEST['redirect_to'],
    412                 __('Wrong verification code, we just sent a new code, please try again!',
    413                     'two-factor-login-telegram'));
     378            $this->login_html($user, $_REQUEST['redirect_to'], $error_message);
    414379            exit;
    415380        }
     
    431396    public function configure_tg()
    432397    {
    433         require(dirname(WP_FACTOR_TG_FILE) . "/sections/configure_tg.php");
     398        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/configuration.php");
    434399    }
    435400
     
    439404    public function show_telegram_logs()
    440405    {
    441         $logs = get_option('telegram_bot_logs', array());
    442 
    443         // Handle clear logs action
    444         if (isset($_POST['clear_logs']) && wp_verify_nonce($_POST['_wpnonce'], 'clear_telegram_logs')) {
    445             delete_option('telegram_bot_logs');
    446             $logs = array();
    447             echo '<div class="notice notice-success is-dismissible"><p>' . __('Logs cleared successfully.', 'two-factor-login-telegram') . '</p></div>';
    448         }
    449 
    450         ?>
    451         <div class="wrap">
    452             <h1><?php _e('Telegram Bot Logs', 'two-factor-login-telegram'); ?></h1>
    453 
    454             <form method="post">
    455                 <?php wp_nonce_field('clear_telegram_logs'); ?>
    456                 <input type="submit" name="clear_logs" class="button button-secondary" value="<?php _e('Clear Logs', 'two-factor-login-telegram'); ?>" onclick="return confirm('<?php _e('Are you sure you want to clear all logs?', 'two-factor-login-telegram'); ?>')">
    457             </form>
    458 
    459             <br>
    460 
    461             <?php if (empty($logs)): ?>
    462                 <p><?php _e('No logs available.', 'two-factor-login-telegram'); ?></p>
    463             <?php else: ?>
    464                 <table class="wp-list-table widefat fixed striped">
    465                     <thead>
    466                         <tr>
    467                             <th style="width: 15%;"><?php _e('Timestamp', 'two-factor-login-telegram'); ?></th>
    468                             <th style="width: 15%;"><?php _e('Action', 'two-factor-login-telegram'); ?></th>
    469                             <th><?php _e('Data', 'two-factor-login-telegram'); ?></th>
    470                         </tr>
    471                     </thead>
    472                     <tbody>
    473                         <?php foreach ($logs as $log): ?>
    474                             <tr>
    475                                 <td><?php echo esc_html($log['timestamp']); ?></td>
    476                                 <td><?php echo esc_html($log['action']); ?></td>
    477                                 <td>
    478                                     <details>
    479                                         <summary><?php _e('View details', 'two-factor-login-telegram'); ?></summary>
    480                                         <pre style="background: #f1f1f1; padding: 10px; margin-top: 10px; overflow-x: auto;"><?php echo esc_html(print_r($log['data'], true)); ?></pre>
    481                                     </details>
    482                                 </td>
    483                             </tr>
    484                         <?php endforeach; ?>
    485                     </tbody>
    486                 </table>
    487             <?php endif; ?>
    488         </div>
    489         <?php
     406        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/logs-page.php");
    490407    }
    491408
     
    502419    }
    503420
    504     function delete_transients($option_name, $old_value, $new_value)
    505     {
    506         if ($this->namespace === $option_name) {
    507             delete_transient(WP_FACTOR_TG_GETME_TRANSIENT);
    508 
    509             if (!empty($new_value['bot_token'])) {
    510                 $webhook_url = rest_url('telegram/v1/webhook');
    511                 $this->telegram->set_bot_token($new_value['bot_token'])->set_webhook($webhook_url);
    512             }
    513         }
     421    function sanitize_settings($input)
     422    {
     423        // Sanitize input values
     424        $sanitized = array();
     425
     426        if (isset($input['bot_token'])) {
     427            $sanitized['bot_token'] = sanitize_text_field($input['bot_token']);
     428        }
     429
     430        if (isset($input['chat_id'])) {
     431            $sanitized['chat_id'] = sanitize_text_field($input['chat_id']);
     432        }
     433
     434        if (isset($input['enabled'])) {
     435            $sanitized['enabled'] = $input['enabled'] === '1' ? '1' : '0';
     436        }
     437
     438        if (isset($input['show_site_name'])) {
     439            $sanitized['show_site_name'] = $input['show_site_name'] === '1' ? '1' : '0';
     440        }
     441
     442        if (isset($input['show_site_url'])) {
     443            $sanitized['show_site_url'] = $input['show_site_url'] === '1' ? '1' : '0';
     444        }
     445
     446        if (isset($input['delete_data_on_deactivation'])) {
     447            $sanitized['delete_data_on_deactivation'] = $input['delete_data_on_deactivation'] === '1' ? '1' : '0';
     448        }
     449
     450        // Delete transients when settings are updated
     451        delete_transient(WP_FACTOR_TG_GETME_TRANSIENT);
     452
     453        // Set webhook if bot token is provided
     454        if (!empty($sanitized['bot_token'])) {
     455            $webhook_url = rest_url('telegram/v1/webhook');
     456            $this->telegram->set_bot_token($sanitized['bot_token'])->set_webhook($webhook_url);
     457        }
     458
     459        return $sanitized;
    514460    }
    515461
    516462    function tg_register_settings()
    517463    {
    518         register_setting($this->namespace, $this->namespace);
     464        register_setting($this->namespace, $this->namespace, array(
     465            'sanitize_callback' => array($this, 'sanitize_settings')
     466        ));
    519467
    520468        add_settings_section($this->namespace . '_section',
    521469            __('Telegram Configuration', "two-factor-login-telegram"), '',
     470            $this->namespace . '.php');
     471
     472        add_settings_section($this->namespace . '_failed_login_section',
     473            __('Failed Login Report', "two-factor-login-telegram"), array($this, 'failed_login_section_callback'),
     474            $this->namespace . '.php');
     475
     476        add_settings_section($this->namespace . '_data_management_section',
     477            __('Data Management', "two-factor-login-telegram"), array($this, 'data_management_section_callback'),
    522478            $this->namespace . '.php');
    523479
     
    539495            $field_args);
    540496
     497        // Move Chat ID to Failed Login Report section
    541498        $field_args = array(
    542499            'type' => 'text',
    543500            'id' => 'chat_id',
    544501            'name' => 'chat_id',
    545             'desc' => __('Chat ID (Telegram) for failed login report.',
     502            'desc' => __('Enter your Telegram Chat ID to receive notifications about failed login attempts.',
    546503                "two-factor-login-telegram"),
    547504            'std' => '',
     
    551508
    552509        add_settings_field('chat_id',
    553             __('Chat ID', "two-factor-login-telegram"), array(
     510            __('Chat ID for Reports', "two-factor-login-telegram"), array(
    554511                $this,
    555512                'tg_display_setting',
    556             ), $this->namespace . '.php', $this->namespace . '_section',
     513            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
    557514            $field_args);
    558515
     
    575532            $field_args);
    576533
     534        // Move Show site name to Failed Login Report section
    577535        $field_args = array(
    578536            'type' => 'checkbox',
    579537            'id' => 'show_site_name',
    580538            'name' => 'show_site_name',
    581             'desc' => __('Select this checkbox to show site name on failed login attempt message.<br>This option is useful when you use same bot in several sites.',
     539            'desc' => __('Include site name in failed login notifications.<br>Useful when using the same bot for multiple sites.',
    582540                'two-factor-login-telegram'),
    583541            'std' => '',
     
    587545
    588546        add_settings_field('show_site_name',
    589             __('Show site name?', 'two-factor-login-telegram'), array(
     547            __('Show Site Name', 'two-factor-login-telegram'), array(
    590548                $this,
    591549                'tg_display_setting',
    592             ), $this->namespace . '.php', $this->namespace . '_section',
     550            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
    593551            $field_args);
    594552
     553        // Move Show site URL to Failed Login Report section
    595554        $field_args = array(
    596555            'type' => 'checkbox',
    597556            'id' => 'show_site_url',
    598557            'name' => 'show_site_url',
    599             'desc' => __('Select this checkbox to show site URL on failed login attempt message.<br>This option is useful when you use same bot in several sites.',
     558            'desc' => __('Include site URL in failed login notifications.<br>Useful when using the same bot for multiple sites.',
    600559                'two-factor-login-telegram'),
    601560            'std' => '',
     
    605564
    606565        add_settings_field('show_site_url',
    607             __('Show site URL?', 'two-factor-login-telegram'), array(
     566            __('Show Site URL', 'two-factor-login-telegram'), array(
    608567                $this,
    609568                'tg_display_setting',
    610             ), $this->namespace . '.php', $this->namespace . '_section',
     569            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
     570            $field_args);
     571
     572        // Add data cleanup option to Data Management section
     573        $field_args = array(
     574            'type' => 'checkbox',
     575            'id' => 'delete_data_on_deactivation',
     576            'name' => 'delete_data_on_deactivation',
     577            'desc' => __('Delete all plugin data when the plugin is deactivated.<br><strong>Warning:</strong> This will permanently remove all settings, user configurations, authentication codes, and logs.',
     578                'two-factor-login-telegram'),
     579            'std' => '',
     580            'label_for' => 'delete_data_on_deactivation',
     581            'class' => 'css_class',
     582        );
     583
     584        add_settings_field('delete_data_on_deactivation',
     585            __('Delete Data on Deactivation', 'two-factor-login-telegram'), array(
     586                $this,
     587                'tg_display_setting',
     588            ), $this->namespace . '.php', $this->namespace . '_data_management_section',
    611589            $field_args);
    612590    }
     
    680658
    681659    /**
     660     * Failed login section callback
     661     */
     662    public function failed_login_section_callback()
     663    {
     664        echo '<p>' . __('Configure how to receive notifications when someone fails to log in to your site.', 'two-factor-login-telegram') . '</p>';
     665    }
     666
     667    /**
     668     * Data management section callback
     669     */
     670    public function data_management_section_callback()
     671    {
     672        echo '<p>' . __('Manage plugin data and cleanup options.', 'two-factor-login-telegram') . '</p>';
     673    }
     674
     675    /**
    682676     * Action links
    683677     *
     
    735729    public function tg_add_two_factor_fields($user)
    736730    {
    737         ?>
    738         <h3 id="wptl"><?php
    739             _e('2FA with Telegram',
    740                 'two-factor-login-telegram'); ?></h3>
    741 
    742         <table class="form-table">
    743 
    744             <tr>
    745                 <th>
    746                     <label for="tg_wp_factor_enabled"><?php
    747                         _e('Enable 2FA',
    748                             'two-factor-login-telegram'); ?>
    749                     </label>
    750                 </th>
    751                 <td colspan="2">
    752                     <input type="hidden" name="tg_wp_factor_valid"
    753                            id="tg_wp_factor_valid" value="<?php
    754                     echo (int)(esc_attr(get_the_author_meta('tg_wp_factor_enabled',
    755                             $user->ID)) === "1"); ?>">
    756                     <input type="checkbox" name="tg_wp_factor_enabled"
    757                            id="tg_wp_factor_enabled" value="1"
    758                            class="regular-text" <?php
    759                     echo checked(esc_attr(get_the_author_meta('tg_wp_factor_enabled',
    760                         $user->ID)), 1); ?> /><br/>
    761                 </td>
    762             </tr>
    763 
    764             <tr>
    765 
    766                 <td colspan="3">
    767 
    768                     <?php
    769                     $username = $this->telegram->get_me()->username;
    770                     ?>
    771 
    772                     <div>
    773 
    774                         <ol>
    775                             <li>
    776                                 <?php
    777                                 printf(__('Open a conversation with %s and press on <strong>Start</strong>',
    778                                     'two-factor-login-telegram'),
    779                                     '<a href="https://telegram.me/' . $username
    780                                     . '" target="_blank">@' . $username . '</a>');
    781                                 ?>
    782                             </li>
    783 
    784                             <li>
    785                                 <?php
    786                                 printf(__('Type command %s to obtain your Chat ID.',
    787                                     "two-factor-login-telegram"),
    788                                     '<code>/get_id</code>');
    789                                 ?>
    790                             </li>
    791                             <li>
    792                                 <?php
    793                                 _e("The bot will reply with your <strong>Chat ID</strong> number",
    794                                     'two-factor-login-telegram');
    795                                 ?>
    796                             </li>
    797 
    798                             <li><?php
    799                                 _e('Copy your Chat ID and paste it below, then press <strong>Submit code</strong>',
    800                                     'two-factor-login-telegram'); ?></li>
    801                         </ol>
    802                     </div>
    803                 </td>
    804 
    805             </tr>
    806 
    807             <tr>
    808                 <th>
    809                     <label for="tg_wp_factor_chat_id"><?php
    810                         _e('Telegram Chat ID',
    811                             'two-factor-login-telegram'); ?>
    812                     </label></th>
    813                 <td>
    814                     <input type="text" name="tg_wp_factor_chat_id"
    815                            id="tg_wp_factor_chat_id" value="<?php
    816                     echo esc_attr(get_the_author_meta('tg_wp_factor_chat_id',
    817                         $user->ID)); ?>" class="regular-text"/><br/>
    818                     <span class="description"><?php
    819                         _e('Put your Telegram Chat ID',
    820                             'two-factor-login-telegram'); ?></span>
    821                 </td>
    822                 <td>
    823                     <button class="button" id="tg_wp_factor_chat_id_send"><?php
    824                         _e("Submit code",
    825                             "two-factor-login-telegram"); ?></button>
    826                 </td>
    827             </tr>
    828 
    829             <tr id="factor-chat-confirm">
    830                 <th>
    831                     <label for="tg_wp_factor_chat_id_confirm"><?php
    832                         _e('Confirmation code',
    833                             'two-factor-login-telegram'); ?>
    834                     </label></th>
    835                 <td>
    836                     <input type="text" name="tg_wp_factor_chat_id_confirm"
    837                            id="tg_wp_factor_chat_id_confirm" value=""
    838                            class="regular-text"/><br/>
    839                     <span class="description"><?php
    840                         _e('Please enter the confirmation code you received on Telegram',
    841                             'two-factor-login-telegram'); ?></span>
    842                 </td>
    843                 <td>
    844                     <button class="button" id="tg_wp_factor_chat_id_check"><?php
    845                         _e("Validate",
    846                             "two-factor-login-telegram"); ?></button>
    847                 </td>
    848             </tr>
    849             <tr id="factor-chat-response">
    850                 <td colspan="3">
    851                     <div class="wpft-notice wpft-notice-warning">
    852                         <p></p>
    853                     </div>
    854                 </td>
    855             </tr>
    856         </table>
    857         <?php
     731        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/user-2fa-form.php");
    858732    }
    859733
     
    878752                "sendtoken_nonce" => wp_create_nonce('ajax-sendtoken-nonce'),
    879753                "tokencheck_nonce" => wp_create_nonce('ajax-tokencheck-nonce'),
    880                 "spinner" => admin_url("/images/spinner.gif")
     754                "spinner" => admin_url("/images/spinner.gif"),
     755                // Translated messages
     756                "invalid_chat_id" => __('Please enter a valid Chat ID', 'two-factor-login-telegram'),
     757                "enter_confirmation_code" => __('Please enter the confirmation code', 'two-factor-login-telegram'),
     758                "setup_completed" => __('✅ 2FA setup completed successfully!', 'two-factor-login-telegram'),
     759                "code_sent" => __('✅ Code sent! Check your Telegram', 'two-factor-login-telegram'),
     760                "modifying_setup" => __('⚠️ Modifying 2FA configuration - validation required', 'two-factor-login-telegram')
    881761            ));
    882762
     
    954834            __("Use this code to complete your 2FA setup in WordPress, or click the button below:", "two-factor-login-telegram")
    955835        );
    956        
     836
    957837        // Create inline keyboard with validation button
    958838        $reply_markup = null;
    959839        if ($current_user_id) {
    960840            $nonce = wp_create_nonce('telegram_validate_' . $current_user_id . '_' . $auth_code);
    961             $validation_url = admin_url('admin.php?page=tg-conf&action=telegram_validate&user_id=' . $current_user_id . '&token=' . $auth_code . '&nonce=' . $nonce);
    962            
     841            $validation_url = admin_url('options-general.php?page=tg-conf&action=telegram_validate&chat_id='.$_POST['chat_id'].'&user_id=' . $current_user_id . '&token=' . $auth_code . '&nonce=' . $nonce);
     842
    963843            $reply_markup = array(
    964844                'inline_keyboard' => array(
     
    972852            );
    973853        }
    974        
     854
    975855        $send = $tg->send_with_keyboard($validation_message, $_POST['chat_id'], $reply_markup);
    976856
     
    1077957    }
    1078958
    1079     public function tg_save_custom_user_profile_fields($user_id)
     959    /**
     960     * Save user's 2FA settings
     961     *
     962     * @param int $user_id User ID
     963     * @param string $chat_id Telegram Chat ID
     964     * @param bool $enabled Whether 2FA is enabled
     965     * @return bool Success status
     966     */
     967    public function save_user_2fa_settings($user_id, $chat_id, $enabled = true)
    1080968    {
    1081969        if (!current_user_can('edit_user', $user_id)) {
     
    1083971        }
    1084972
     973        if (empty($chat_id)) {
     974            return false;
     975        }
     976
     977        update_user_meta($user_id, 'tg_wp_factor_chat_id', sanitize_text_field($chat_id));
     978        update_user_meta($user_id, 'tg_wp_factor_enabled', $enabled ? '1' : '0');
     979
     980        return true;
     981    }
     982
     983    public function tg_save_custom_user_profile_fields($user_id)
     984    {
    1085985        if ($_POST['tg_wp_factor_valid'] == 0
    1086986            || $_POST['tg_wp_factor_chat_id'] == ""
     
    1089989        }
    1090990
    1091         update_user_meta($user_id, 'tg_wp_factor_chat_id',
    1092             $_POST['tg_wp_factor_chat_id']);
    1093         update_user_meta($user_id, 'tg_wp_factor_enabled',
    1094             $_POST['tg_wp_factor_enabled']);
    1095 
    1096         return true;
     991        return $this->save_user_2fa_settings(
     992            $user_id,
     993            $_POST['tg_wp_factor_chat_id'],
     994            isset($_POST['tg_wp_factor_enabled'])
     995        );
    1097996    }
    1098997
     
    11691068    }
    11701069
     1070    private function create_or_update_activities_table()
     1071    {
     1072        global $wpdb;
     1073
     1074        $table_name = $wpdb->prefix . 'wp2fat_activities';
     1075
     1076        $charset_collate = $wpdb->get_charset_collate();
     1077        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
     1078        id mediumint(9) NOT NULL AUTO_INCREMENT, 
     1079        timestamp datetime NOT NULL,
     1080        action varchar(100) NOT NULL,
     1081        data longtext,
     1082        PRIMARY KEY (id),
     1083        KEY timestamp (timestamp),
     1084        KEY action (action)
     1085    ) $charset_collate";
     1086
     1087        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
     1088
     1089        // Esegue la query per creare/aggiornare la tabella
     1090        dbDelta($sql);
     1091    }
     1092
     1093    private function migrate_logs_to_activities_table()
     1094    {
     1095        global $wpdb;
     1096
     1097        // Check if old logs exist in option
     1098        $old_logs = get_option('telegram_bot_logs', array());
     1099
     1100        if (!empty($old_logs)) {
     1101            $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1102
     1103            // Migrate each log entry
     1104            foreach ($old_logs as $log) {
     1105                $wpdb->insert(
     1106                    $activities_table,
     1107                    array(
     1108                        'timestamp' => $log['timestamp'],
     1109                        'action' => $log['action'],
     1110                        'data' => maybe_serialize($log['data'])
     1111                    ),
     1112                    array('%s', '%s', '%s')
     1113                );
     1114            }
     1115
     1116            // Delete the old option after migration
     1117            delete_option('telegram_bot_logs');
     1118        }
     1119    }
     1120
    11711121    function plugin_activation()
    11721122    {
    11731123        $this->create_or_update_telegram_auth_codes_table();
     1124        $this->create_or_update_activities_table();
     1125        $this->migrate_logs_to_activities_table();
    11741126        update_option('wp_factor_plugin_version', WP_FACTOR_PLUGIN_VERSION);
    11751127
    1176         // Flush rewrite rules to add our webhook endpoint
     1128        // Add rewrite rules before flushing
     1129        $this->add_telegram_rewrite_rules();
     1130
     1131        // Flush rewrite rules to add our new rules
    11771132        flush_rewrite_rules();
     1133    }
     1134
     1135    function plugin_deactivation()
     1136    {
     1137        // Check if data cleanup is enabled
     1138        $plugin_options = get_option($this->namespace, array());
     1139
     1140        if (isset($plugin_options['delete_data_on_deactivation']) && $plugin_options['delete_data_on_deactivation'] === '1') {
     1141            $this->cleanup_all_plugin_data();
     1142        }
     1143
     1144        // Always flush rewrite rules on deactivation
     1145        flush_rewrite_rules();
     1146    }
     1147
     1148    private function cleanup_all_plugin_data()
     1149    {
     1150        global $wpdb;
     1151
     1152        // Delete plugin settings
     1153        delete_option($this->namespace);
     1154        delete_option('wp_factor_plugin_version');
     1155        delete_option('telegram_bot_logs'); // For backward compatibility
     1156
     1157        // Delete auth codes table
     1158        $auth_codes_table = $wpdb->prefix . 'telegram_auth_codes';
     1159        $wpdb->query("DROP TABLE IF EXISTS $auth_codes_table");
     1160
     1161        // Delete activities table
     1162        $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1163        $wpdb->query("DROP TABLE IF EXISTS $activities_table");
     1164
     1165        // Delete user meta data for all users
     1166        $wpdb->delete(
     1167            $wpdb->usermeta,
     1168            array(
     1169                'meta_key' => 'tg_wp_factor_chat_id'
     1170            )
     1171        );
     1172
     1173        $wpdb->delete(
     1174            $wpdb->usermeta,
     1175            array(
     1176                'meta_key' => 'tg_wp_factor_enabled'
     1177            )
     1178        );
     1179
     1180        // Delete all transients related to the plugin
     1181        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_wp2fa_telegram_authcode_%'");
     1182        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_wp2fa_telegram_authcode_%'");
     1183        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_" . WP_FACTOR_TG_GETME_TRANSIENT . "%'");
     1184        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_" . WP_FACTOR_TG_GETME_TRANSIENT . "%'");
    11781185    }
    11791186
     
    11841191        if ($installed_version !== WP_FACTOR_PLUGIN_VERSION) {
    11851192            $this->create_or_update_telegram_auth_codes_table();
     1193            $this->create_or_update_activities_table();
     1194            $this->migrate_logs_to_activities_table();
    11861195            update_option('wp_factor_plugin_version', WP_FACTOR_PLUGIN_VERSION);
    11871196        }
     
    12011210
    12021211        register_activation_hook(WP_FACTOR_TG_FILE, array($this, 'plugin_activation'));
     1212        register_deactivation_hook(WP_FACTOR_TG_FILE, array($this, 'plugin_deactivation'));
    12031213        add_action('plugins_loaded', array($this, 'check_plugin_update'));
    12041214
     
    12091219                . plugin_basename(WP_FACTOR_TG_FILE),
    12101220                array($this, 'action_links'));
    1211             add_action('updated_option', array($this, 'delete_transients'),
    1212                 10, 3);
    12131221        }
    12141222
     
    12231231        }
    12241232
    1225         add_action('show_user_profile',
    1226             array($this, 'tg_add_two_factor_fields'));
    1227         add_action('edit_user_profile',
    1228             array($this, 'tg_add_two_factor_fields'));
     1233        if ($this->is_valid_bot()) {
     1234            add_action('show_user_profile',
     1235                array($this, 'tg_add_two_factor_fields'));
     1236            add_action('edit_user_profile',
     1237                array($this, 'tg_add_two_factor_fields'));
     1238        }
    12291239
    12301240        add_action('personal_options_update',
     
    12421252        // Add REST API endpoint for Telegram webhook
    12431253        add_action('rest_api_init', array($this, 'register_telegram_webhook_route'));
     1254
     1255        // Add rewrite rules for Telegram confirmation URLs
     1256        add_action('init', array($this, 'add_telegram_rewrite_rules'));
     1257        add_action('parse_request', array($this, 'parse_telegram_request'));
     1258
    12441259    }
    12451260
     
    12531268            'permission_callback' => '__return_true',
    12541269        ));
    1255 
    1256         register_rest_route('telegram/v1', '/confirm/(?P<user_id>\d+)/(?P<token>[a-zA-Z0-9]+)', array(
    1257             'methods' => 'GET',
    1258             'callback' => array($this, 'handle_telegram_confirmation'),
    1259             'permission_callback' => '__return_true',
    1260             'args' => array(
    1261                 'user_id' => array(
    1262                     'validate_callback' => function($param, $request, $key) {
    1263                         return is_numeric($param);
    1264                     }
    1265                 ),
    1266                 'token' => array(
    1267                     'validate_callback' => function($param, $request, $key) {
    1268                         return preg_match('/^[a-zA-Z0-9]+$/', $param);
    1269                     }
    1270                 ),
    1271                 'nonce' => array(
    1272                     'required' => true,
    1273                 )
     1270    }
     1271
     1272    /**
     1273     * Add rewrite rules for Telegram confirmation URLs
     1274     */
     1275    public function add_telegram_rewrite_rules() {
     1276        add_rewrite_rule(
     1277            '^telegram-confirm/([0-9]+)/([a-zA-Z0-9]+)/?$',
     1278            'index.php?telegram_confirm=1&user_id=$matches[1]&token=$matches[2]',
     1279            'top'
     1280        );
     1281
     1282        // Add query vars
     1283        add_filter('query_vars', function($vars) {
     1284            $vars[] = 'telegram_confirm';
     1285            $vars[] = 'user_id';
     1286            $vars[] = 'token';
     1287            return $vars;
     1288        });
     1289    }
     1290
     1291    /**
     1292     * Parse Telegram confirmation requests
     1293     */
     1294    public function parse_telegram_request() {
     1295        global $wp;
     1296
     1297        if (isset($wp->query_vars['telegram_confirm']) && $wp->query_vars['telegram_confirm'] == 1) {
     1298            $user_id = intval($wp->query_vars['user_id']);
     1299            $token = sanitize_text_field($wp->query_vars['token']);
     1300            $nonce = sanitize_text_field($_GET['nonce'] ?? '');
     1301
     1302            $this->handle_telegram_confirmation_direct($user_id, $token, $nonce);
     1303        }
     1304    }
     1305
     1306    /**
     1307     * Handle Telegram confirmation via direct URL (not REST API)
     1308     */
     1309    public function handle_telegram_confirmation_direct($user_id, $token, $nonce) {
     1310        // Verify nonce
     1311        if (!wp_verify_nonce($nonce, 'telegram_confirm_' . $user_id . '_' . $token)) {
     1312            $this->log_telegram_action('confirmation_failed', array(
     1313                'user_id' => $user_id,
     1314                'token' => $token,
     1315                'reason' => 'invalid_nonce'
     1316            ));
     1317
     1318            // Include error template
     1319            require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-security-failed.php");
     1320        }
     1321
     1322        // Validate the token
     1323        $authcode_validation = $this->is_valid_authcode($token, $user_id);
     1324
     1325        if ('valid' !== $authcode_validation) {
     1326            if ($authcode_validation === 'expired') {
     1327                $log_reason = 'expired_token';
     1328            } else {
     1329                $log_reason = 'invalid_token';
     1330            }
     1331
     1332            $this->log_telegram_action('confirmation_failed', array(
     1333                'user_id' => $user_id,
     1334                'token' => $token,
     1335                'reason' => $log_reason
     1336            ));
     1337
     1338            // Include appropriate error template
     1339            if ($authcode_validation === 'expired') {
     1340                require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-expired-token.php");
     1341            } else {
     1342                require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-invalid-token.php");
     1343            }
     1344        }
     1345
     1346        // Get user
     1347        $user = get_userdata($user_id);
     1348        if (!$user) {
     1349            require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-invalid-token.php");
     1350        }
     1351
     1352        // Log the user in
     1353        wp_set_auth_cookie($user_id, false);
     1354
     1355        $this->log_telegram_action('confirmation_success', array(
     1356            'user_id' => $user_id,
     1357            'user_login' => $user->user_login,
     1358            'token' => $token,
     1359            'method' => 'direct_confirmation'
     1360        ));
     1361
     1362        // Redirect to admin or specified location
     1363        $redirect_url = apply_filters('telegram_confirmation_redirect_url', admin_url(), $user);
     1364
     1365        // Redirect directly
     1366        wp_safe_redirect($redirect_url);
     1367        exit;
     1368    }
     1369
     1370    /**
     1371     * Log Telegram bot actions
     1372     */
     1373    public function log_telegram_action($action, $data = array()) {
     1374        global $wpdb;
     1375
     1376        $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1377
     1378        // Insert new log entry
     1379        $wpdb->insert(
     1380            $activities_table,
     1381            array(
     1382                'timestamp' => current_time('mysql'),
     1383                'action' => $action,
     1384                'data' => maybe_serialize($data)
    12741385            ),
    1275         ));
    1276     }
    1277 
    1278     /**
    1279      * Log Telegram bot actions
    1280      */
    1281     private function log_telegram_action($action, $data = array()) {
    1282         $log_entry = array(
    1283             'timestamp' => current_time('mysql'),
    1284             'action' => $action,
    1285             'data' => $data
    1286         );
    1287 
    1288         $logs = get_option('telegram_bot_logs', array());
    1289         array_unshift($logs, $log_entry);
    1290 
    1291         // Keep only last 100 log entries
    1292         $logs = array_slice($logs, 0, 100);
    1293 
    1294         update_option('telegram_bot_logs', $logs);
     1386            array('%s', '%s', '%s')
     1387        );
     1388
     1389        // Clean up old entries - keep only last 1000 entries
     1390        $wpdb->query("DELETE FROM $activities_table WHERE id NOT IN (SELECT id FROM (SELECT id FROM $activities_table ORDER BY timestamp DESC LIMIT 1000) temp_table)");
    12951391    }
    12961392
     
    13511447    }
    13521448
    1353     /**
    1354      * Handle Telegram confirmation via link
    1355      */
    1356     public function handle_telegram_confirmation($request) {
    1357         $user_id = intval($request['user_id']);
    1358         $token = sanitize_text_field($request['token']);
    1359         $nonce = sanitize_text_field($request->get_param('nonce'));
    1360        
    1361         // Verify nonce
    1362         if (!wp_verify_nonce($nonce, 'telegram_confirm_' . $user_id . '_' . $token)) {
    1363             $this->log_telegram_action('confirmation_failed', array(
    1364                 'user_id' => $user_id,
    1365                 'token' => $token,
    1366                 'reason' => 'invalid_nonce'
    1367             ));
    1368             return new WP_Error('invalid_nonce', __('Security check failed.', 'two-factor-login-telegram'), array('status' => 403));
    1369         }
    1370        
    1371         // Validate the token
    1372         if (!$this->is_valid_authcode($token, $user_id)) {
    1373             $this->log_telegram_action('confirmation_failed', array(
    1374                 'user_id' => $user_id,
    1375                 'token' => $token,
    1376                 'reason' => 'invalid_token'
    1377             ));
    1378             return new WP_Error('invalid_token', __('Invalid or expired token.', 'two-factor-login-telegram'), array('status' => 400));
    1379         }
    1380        
    1381         // Get user
    1382         $user = get_userdata($user_id);
    1383         if (!$user) {
    1384             return new WP_Error('invalid_user', __('Invalid user.', 'two-factor-login-telegram'), array('status' => 400));
    1385         }
    1386        
    1387         // Log the user in
    1388         wp_set_auth_cookie($user_id, false);
    1389        
    1390         $this->log_telegram_action('confirmation_success', array(
    1391             'user_id' => $user_id,
    1392             'user_login' => $user->user_login,
    1393             'token' => $token,
    1394             'method' => 'link_confirmation'
    1395         ));
    1396        
    1397         // Redirect to admin or specified location
    1398         $redirect_url = apply_filters('telegram_confirmation_redirect_url', admin_url(), $user);
    1399        
    1400         // Redirect directly instead of returning JSON
    1401         wp_safe_redirect($redirect_url);
    1402         exit;
    1403     }
     1449
    14041450
    14051451}
  • two-factor-login-telegram/tags/3.5.0/includes/class-wp-telegram.php

    r3331421 r3332295  
    148148        if ($user_id) {
    149149            $nonce = wp_create_nonce('telegram_confirm_' . $user_id . '_' . $token);
    150             $confirmation_url = site_url('/wp-json/telegram/v1/confirm/' . $user_id . '/' . $token . '?nonce=' . $nonce);
     150            $confirmation_url = home_url('/telegram-confirm/' . $user_id . '/' . $token . '/?nonce=' . $nonce);
    151151           
    152152            $reply_markup = array(
  • two-factor-login-telegram/tags/3.5.0/two-factor-telegram.php

    r3331421 r3332295  
    55 * Plugin URI: https://blog.dueclic.com/wordpress-autenticazione-due-fattori-telegram/
    66 * Description: This plugin enables two factor authentication with Telegram by increasing your website security and sends an alert every time a wrong login occurs.
    7  * Version: 3.4
     7 * Version: 3.5.0
    88 * Requires at least: 6.0
    99 * Tested up to: 6.8
     
    2222}
    2323
    24 define('WP_FACTOR_PLUGIN_VERSION', '3.4');
     24define('WP_FACTOR_PLUGIN_VERSION', '3.5.0');
    2525
    2626define('WP_FACTOR_AUTHCODE_EXPIRE_SECONDS', 60 * 20);
  • two-factor-login-telegram/trunk/README.md

    r3331421 r3332295  
    55Requires PHP: 7.0
    66Tested up to: 6.8
    7 Stable tag: 3.4
     7Stable tag: 3.5.0
    88License: GPLv3
    99License URI: http://www.gnu.org/licenses/gpl-3.0.html
     
    5252
    5353== Changelog ==
     54
     55= 3.5.0 =
     56* **Enhanced Logs System**: Replaced simple logs with professional WP_List_Table implementation featuring pagination (10 items per page), sorting, and bulk actions
     57* **Improved User Interface**: Complete UI overhaul with enhanced styling, better form layouts, and improved user experience
     58* **Advanced Database Management**: Migrated to MySQL tables for better performance and reliability instead of WordPress options
     59* **Better Chat ID Validation**: Enhanced Chat ID validation with proper format checking for both user and group chats
     60* **JavaScript Translations**: Implemented proper internationalization for all JavaScript messages using wp_localize_script
     61* **Enhanced User Feedback**: Added contextual status messages during 2FA configuration with clear visual indicators
     62* **Template System**: Introduced dedicated error templates for better error handling and user guidance
     63* **Timestamp Formatting**: Logs now respect WordPress date/time format settings for consistent display
     64* **Bug Fixes**: Fixed duplicate Chat ID input elements issue and improved form validation
     65* **Performance Improvements**: Optimized database queries and reduced memory usage
    5466
    5567= 3.4 =
  • two-factor-login-telegram/trunk/assets/css/wp-factor-telegram-plugin.css

    r2805641 r3332295  
    137137
    138138#wpft-howto .ui-accordion-header {
     139    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     140}
     141
     142/* 2FA Configuration Improvements */
     143#tg-2fa-configuration {
     144    opacity: 0;
     145    max-height: 0;
     146    overflow: hidden;
     147    transition: all 0.3s ease-in-out;
     148    transform: translateY(-10px);
     149}
     150
     151#tg-2fa-configuration.show {
     152    opacity: 1;
     153    max-height: 1000px;
     154    transform: translateY(0);
     155}
     156
     157#tg-2fa-configuration .form-table {
     158    background: #f9f9f9;
     159    border-radius: 8px;
     160    padding: 20px;
     161    margin-top: 15px;
     162    border: 1px solid #e1e1e1;
     163}
     164
     165/* Step indicator */
     166.tg-setup-steps {
     167    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     168    color: white;
     169    padding: 20px;
     170    border-radius: 8px;
     171    margin-bottom: 20px;
     172}
     173
     174.tg-setup-steps ol {
     175    margin: 0;
     176    padding-left: 20px;
     177}
     178
     179.tg-setup-steps li {
     180    margin-bottom: 10px;
     181    line-height: 1.5;
     182}
     183
     184.tg-setup-steps a {
     185    color: #fff;
     186    text-decoration: underline;
     187}
     188
     189.tg-setup-steps strong {
     190    font-weight: 600;
     191}
     192
     193/* Input improvements */
     194#tg_wp_factor_chat_id {
     195    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
     196    font-size: 14px;
     197    padding: 8px 12px;
     198    border: 2px solid #ddd;
     199    border-radius: 6px;
     200    transition: all 0.2s ease;
     201}
     202
     203#tg_wp_factor_chat_id:focus {
     204    border-color: #667eea;
     205    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
     206    outline: none;
     207}
     208
     209#tg_wp_factor_chat_id.input-valid {
     210    border-color: #46b450 !important;
     211    box-shadow: 0 0 0 2px rgba(70, 180, 80, 0.2) !important;
     212}
     213
     214/* Button improvements */
     215.tg-action-button {
     216    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     217    border: none;
     218    color: white;
     219    padding: 10px 20px;
     220    border-radius: 6px;
     221    cursor: pointer;
     222    font-weight: 500;
     223    transition: all 0.2s ease;
     224    position: relative;
     225    overflow: hidden;
     226}
     227
     228.tg-action-button:hover {
     229    transform: translateY(-1px);
     230    box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
     231}
     232
     233.tg-action-button:active {
     234    transform: translateY(0);
     235}
     236
     237.tg-action-button.disabled {
     238    opacity: 0.6;
     239    cursor: not-allowed;
     240    transform: none !important;
     241    box-shadow: none !important;
     242}
     243
     244/* Loading spinner improvements */
     245.load-spinner {
     246    display: inline-block;
     247    margin-left: 10px;
     248    vertical-align: middle;
     249}
     250
     251/* FAQ Section Styling */
     252#wpft-howto {
     253    margin: 20px 0;
     254}
     255
     256#wpft-howto h3 {
    139257    background-color: #389abe;
    140 }
     258    color: white;
     259    padding: 15px 20px;
     260    margin: 20px 0 0 0;
     261    font-size: 16px;
     262    border-radius: 5px 5px 0 0;
     263}
     264
     265#wpft-howto .faq-content {
     266    background-color: #f9f9f9;
     267    padding: 20px;
     268    border: 1px solid #ddd;
     269    border-top: none;
     270    border-radius: 0 0 5px 5px;
     271    margin-bottom: 20px;
     272}
     273
     274#wpft-howto p {
     275    margin: 10px 0;
     276    line-height: 1.6;
     277}
     278
     279#wpft-howto ol {
     280    margin: 15px 0;
     281    padding-left: 20px;
     282}
     283
     284#wpft-howto li {
     285    margin-bottom: 10px;
     286    line-height: 1.5;
     287}
     288
     289#wpft-howto .command {
     290    background: #333;
     291    color: #fff;
     292    padding: 2px 6px;
     293    border-radius: 3px;
     294    font-family: monospace;
     295    font-size: 13px;
     296}
     297
     298#wpft-howto .screenshot-container {
     299    margin: 15px 0;
     300    text-align: center;
     301    padding: 10px;
     302    background: #fff;
     303    border: 1px solid #ddd;
     304    border-radius: 3px;
     305}
     306
     307#wpft-howto .help-screenshot {
     308    max-width: 100%;
     309    height: auto;
     310    border: 1px solid #ccc;
     311}
     312
     313#wpft-howto .notice {
     314    padding: 12px;
     315    margin: 15px 0;
     316    border-left: 4px solid #00a0d2;
     317    background: #f7fcfe;
     318}
     319
     320#wpft-howto .external-link {
     321    color: #0073aa;
     322    text-decoration: none;
     323}
     324
     325#wpft-howto .external-link:hover {
     326    text-decoration: underline;
     327}
     328
     329.load-spinner img {
     330    width: 20px;
     331    height: 20px;
     332}
     333
     334/* Progress indicator */
     335.tg-progress {
     336    background: #f0f0f0;
     337    border-radius: 10px;
     338    height: 6px;
     339    overflow: hidden;
     340    margin: 15px 0;
     341}
     342
     343.tg-progress-bar {
     344    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
     345    height: 100%;
     346    border-radius: 10px;
     347    transition: width 0.3s ease;
     348    width: 0%;
     349}
     350
     351/* Status indicators */
     352.tg-status {
     353    display: inline-flex;
     354    align-items: center;
     355    padding: 8px 12px;
     356    border-radius: 6px;
     357    font-size: 12px;
     358    font-weight: 500;
     359    margin-top: 10px;
     360}
     361
     362.tg-status.success {
     363    background: #d4edda;
     364    color: #155724;
     365    border: 1px solid #c3e6cb;
     366}
     367
     368.tg-status.error {
     369    background: #f8d7da;
     370    color: #721c24;
     371    border: 1px solid #f5c6cb;
     372}
     373
     374.tg-status.warning {
     375    background: #fff3cd;
     376    color: #856404;
     377    border: 1px solid #ffeeba;
     378}
     379
     380.tg-status::before {
     381    content: '';
     382    width: 8px;
     383    height: 8px;
     384    border-radius: 50%;
     385    margin-right: 8px;
     386}
     387
     388.tg-status.success::before {
     389    background: #28a745;
     390}
     391
     392.tg-status.error::before {
     393    background: #dc3545;
     394}
     395
     396.tg-status.warning::before {
     397    background: #ffc107;
     398}
     399
     400/* Confirmation field improvements */
     401#factor-chat-confirm {
     402    background: #f8f9fa;
     403    border-radius: 8px;
     404    padding: 15px;
     405    border: 1px solid #e9ecef;
     406    margin-top: 15px;
     407}
     408
     409#tg_wp_factor_chat_id_confirm {
     410    font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
     411    font-size: 14px;
     412    padding: 8px 12px;
     413    border: 2px solid #ddd;
     414    border-radius: 6px;
     415    transition: all 0.2s ease;
     416}
     417
     418#tg_wp_factor_chat_id_confirm:focus {
     419    border-color: #667eea;
     420    box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.2);
     421    outline: none;
     422}
     423
     424/* Responsive improvements */
     425@media (max-width: 768px) {
     426    #tg-2fa-configuration .form-table td {
     427        display: block;
     428        width: 100%;
     429        padding-bottom: 10px;
     430    }
     431
     432    .tg-action-button {
     433        width: 100%;
     434        margin-top: 10px;
     435    }
     436}
  • two-factor-login-telegram/trunk/assets/js/wp-factor-telegram-plugin.js

    r3168516 r3332295  
    99    var $twbtn = $("#tg_wp_factor_chat_id_send");
    1010    var $twctrl = $("#tg_wp_factor_valid");
     11    var $twenabled = $("#tg_wp_factor_enabled");
     12    var $twconfig = $("#tg-2fa-configuration");
     13    var $tweditbtn = $("#tg-edit-chat-id");
     14    var $twconfigrow = $(".tg-configured-row");
    1115
    1216    var $twfcr = $("#factor-chat-response");
     
    2327    function init() {
    2428
     29        // Handle checkbox toggle for 2FA configuration with smooth animation
     30        $twenabled.on("change", function(evt){
     31            var isConfigured = $twconfigrow.length > 0;
     32
     33            if ($(this).is(":checked") && !isConfigured) {
     34                $twconfig.addClass('show').show();
     35                updateProgress(25);
     36            } else {
     37                $twconfig.removeClass('show');
     38                setTimeout(function() {
     39                    $twconfig.hide();
     40                }, 300);
     41                $twctrl.val(0);
     42                updateProgress(0);
     43                resetStatusIndicators();
     44            }
     45        });
     46
     47        // Handle edit button click (when 2FA is already configured)
     48        $tweditbtn.on("click", function(evt){
     49            evt.preventDefault();
     50
     51            // Hide configured row and show configuration form
     52            $twconfigrow.hide();
     53            $twconfig.addClass('show').show();
     54
     55            // Make the input editable and clear it
     56            $twfci.prop('readonly', false).removeClass('input-valid').css('background', '').val('');
     57
     58            // Reset validation state
     59            $twctrl.val(0);
     60            updateProgress(25);
     61            resetStatusIndicators();
     62           
     63            // Show modifying status message
     64            $('.tg-status.success').removeClass('success').addClass('warning').text(tlj.modifying_setup);
     65        });
     66
     67        // Watch for changes in Chat ID when in edit mode
     68        $twfci.on("input", function(){
     69            var currentValue = $(this).val();
     70            var originalValue = $(this).data('original-value');
     71
     72            // If value changed from original, require re-validation
     73            if (currentValue !== originalValue) {
     74                $twctrl.val(0);
     75                $(this).removeClass('input-valid');
     76            }
     77        });
     78
     79        // Initialize visibility based on checkbox state and configuration
     80        var isConfigured = $twconfigrow.length > 0;
     81        if ($twenabled.is(":checked") && !isConfigured) {
     82            $twconfig.addClass('show').show();
     83            updateProgress(25);
     84        } else {
     85            $twconfig.removeClass('show').hide();
     86        }
     87
     88        // Store original chat ID value for comparison
     89        if ($twfci.length) {
     90            $twfci.data('original-value', $twfci.val());
     91        }
     92
    2593        $twfci.on("change", function(evt){
    2694           $twctrl.val(0);
     95           // Validate Chat ID format (basic validation)
     96           validateChatId($(this).val());
    2797        });
    2898
    2999        $twbtn.on("click", function(evt){
    30 
    31100            evt.preventDefault();
    32101            var chat_id = $twfci.val();
     102
     103            if (!validateChatId(chat_id)) {
     104                showStatus('#chat-id-status', 'error', tlj.invalid_chat_id);
     105                return;
     106            }
     107
    33108            send_tg_token(chat_id);
    34 
    35109        });
    36110
    37111        $twfcheck.on("click", function(evt){
    38 
    39112            evt.preventDefault();
    40113            var token = $twfciconf.val();
    41114            var chat_id = $twfci.val();
     115
     116            if (!token.trim()) {
     117                showStatus('#validation-status', 'error', tlj.enter_confirmation_code);
     118                return;
     119            }
     120
    42121            check_tg_token(token, chat_id);
    43 
    44122        });
    45123
     
    106184                $twfcheck.addClass('disabled').after('<div class="load-spinner"><img src="'+tlj.spinner+'" /></div>');
    107185                $twfcr.hide();
     186                hideStatus('#validation-status');
    108187            },
    109188            dataType: "json",
     
    114193                    $twfci.addClass("input-valid");
    115194                    $twctrl.val(1);
     195                    updateProgress(100);
     196                    showStatus('#validation-status', 'success', tlj.setup_completed);
    116197                }
    117198                else {
    118                     $twfcr.find(".wpft-notice p").text(response.msg);
    119                     $twfcr.show();
     199                    showStatus('#validation-status', 'error', response.msg);
    120200                    $twfci.removeClass("input-valid");
    121201                    $twctrl.val(0);
     
    124204            },
    125205            error: function(xhr, ajaxOptions, thrownError){
    126                 $twfcr.find(".wpft-notice p").text(tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    127                 $twfcr.show();
     206                showStatus('#validation-status', 'error', tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    128207                $twfci.removeClass("input-valid");
    129208            },
     
    152231                $twfcr.hide();
    153232                $twfconf.hide();
     233                hideStatus('#chat-id-status');
    154234            },
    155235            dataType: "json",
     
    159239                    $twfconf.show();
    160240                    $twfci.removeClass("input-valid");
     241                    updateProgress(75);
     242                    showStatus('#chat-id-status', 'success', tlj.code_sent);
    161243                }
    162244                else {
    163                     $twfcr.find(".wpft-notice p").text(response.msg);
    164                     $twfcr.show();
     245                    showStatus('#chat-id-status', 'error', response.msg);
    165246                }
    166247
    167248            },
    168249            error: function(xhr, ajaxOptions, thrownError){
    169                 $twfcr.find(".wpft-notice p").text(tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    170                 $twfcr.show();
     250                showStatus('#chat-id-status', 'error', tlj.ajax_error+" "+thrownError+" ("+xhr.state+")");
    171251            },
    172252            complete: function() {
     
    180260    }
    181261
    182 
     262    // Helper functions
     263    function updateProgress(percentage) {
     264        $('#tg-progress-bar').css('width', percentage + '%');
     265    }
     266
     267    function validateChatId(chatId) {
     268        // Telegram Chat ID validation: must be numeric (positive for users, negative for groups)
     269        if (!chatId || typeof chatId !== 'string') {
     270            return false;
     271        }
     272       
     273        var trimmedId = chatId.trim();
     274        if (trimmedId === '') {
     275            return false;
     276        }
     277       
     278        // Check if it's a valid number (can be negative for groups)
     279        var numericId = parseInt(trimmedId, 10);
     280        return !isNaN(numericId) && trimmedId === numericId.toString();
     281    }
     282
     283    function showStatus(selector, type, message) {
     284        var $status = $(selector);
     285        $status.removeClass('success error warning')
     286               .addClass(type)
     287               .text(message)
     288               .fadeIn(300);
     289    }
     290
     291    function hideStatus(selector) {
     292        $(selector).fadeOut(300);
     293    }
     294
     295    function resetStatusIndicators() {
     296        hideStatus('#chat-id-status');
     297        hideStatus('#validation-status');
     298    }
    183299
    184300}(jQuery);
  • two-factor-login-telegram/trunk/includes/class-wp-factor-telegram-plugin.php

    r3331421 r3332295  
    5656        require_once(dirname(WP_FACTOR_TG_FILE)
    5757            . "/includes/class-wp-telegram.php");
     58        require_once(dirname(WP_FACTOR_TG_FILE)
     59            . "/includes/class-telegram-logs-list-table.php");
    5860    }
    5961
     
    235237
    236238    /**
    237      * Authentication page
    238      *
    239      * @param $user
    240      */
    241 
    242     private function authentication_page($user)
    243     {
    244         require_once(ABSPATH . '/wp-admin/includes/template.php');
    245         ?>
    246 
    247         <p class="notice notice-warning">
    248             <?php
    249             _e("Enter the code sent to your Telegram account.",
    250                 "two-factor-login-telegram");
    251             ?>
    252         </p>
    253         <p>
    254             <label for="authcode" style="padding-top:1em">
    255                 <?php
    256                 _e("Authentication code:", "two-factor-login-telegram");
    257                 ?>
    258             </label>
    259             <input type="text" name="authcode" id="authcode" class="input"
    260                    value="" size="5"/>
    261         </p>
    262         <?php
    263         submit_button(__('Login with Telegram',
    264             'two-factor-login-telegram'));
    265     }
    266 
    267     /**
    268239     * Login HTML Page
    269240     *
     
    280251        }
    281252
    282         login_header();
    283 
    284         if (!empty($error_msg)) {
    285             echo '<div id="login_error"><strong>' . esc_html($error_msg)
    286                 . '</strong><br /></div>';
    287         }
    288253        // Filter hook to add a custom logo to the 2FA login screen
    289254        $plugin_logo = apply_filters('two_factor_login_telegram_logo',
    290255            plugins_url('assets/img/plugin_logo.png', WP_FACTOR_TG_FILE));
    291         ?>
    292 
    293         <style>
    294             body.login div#login h1 a {
    295                 background-image: url("<?php echo esc_url($plugin_logo); ?>");
    296             }
    297         </style>
    298 
    299         <form name="validate_tg" id="loginform" action="<?php
    300         echo esc_url(site_url('wp-login.php?action=validate_tg',
    301             'login_post')); ?>" method="post" autocomplete="off">
    302             <input type="hidden" name="nonce"
    303                    value="<?php echo wp_create_nonce('wp2fa_telegram_auth_nonce_' . $user->ID); ?>">
    304             <input type="hidden" name="wp-auth-id" id="wp-auth-id" value="<?php
    305             echo esc_attr($user->ID); ?>"/>
    306             <input type="hidden" name="redirect_to" value="<?php
    307             echo esc_attr($redirect_to); ?>"/>
    308             <input type="hidden" name="rememberme" id="rememberme" value="<?php
    309             echo esc_attr($rememberme); ?>"/>
    310 
    311             <?php
    312             $this->authentication_page($user); ?>
    313         </form>
    314 
    315         <p id="backtoblog">
    316             <a href="<?php
    317             echo esc_url(home_url('/')); ?>" title="<?php
    318             __("Are you lost?", "two-factor-login-telegram"); ?>"><?php
    319                 echo sprintf(__('&larr; Back to %s',
    320                     'two-factor-login-telegram'),
    321                     get_bloginfo('title', 'display')); ?></a>
    322         </p>
    323 
    324         <?php
    325         do_action('login_footer'); ?>
    326         <div class="clear"></div>
    327         </body>
    328 
    329         </html>
    330         <?php
     256
     257        require_once(ABSPATH . '/wp-admin/includes/template.php');
     258        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/login-form.php");
    331259    }
    332260
     
    361289        $hashed_auth_code = hash('sha256', $authcode);
    362290        $current_datetime = current_time('mysql');
    363         $query = $wpdb->prepare(
     291
     292        // Check if token exists for this user
     293        $token_exists_query = $wpdb->prepare(
    364294            "SELECT COUNT(*)
    365         FROM $table_name
    366         WHERE auth_code = %s
    367         AND user_id = %d
    368         AND expiration_date > %s",
     295            FROM $table_name
     296            WHERE auth_code = %s
     297            AND user_id = %d",
     298            $hashed_auth_code, $user_id
     299        );
     300
     301        $token_exists = ($wpdb->get_var($token_exists_query) > 0);
     302
     303        if (!$token_exists) {
     304            return 'invalid'; // Invalid token
     305        }
     306
     307        // Check if token is not expired
     308        $valid_token_query = $wpdb->prepare(
     309            "SELECT COUNT(*)
     310            FROM $table_name
     311            WHERE auth_code = %s
     312            AND user_id = %d
     313            AND expiration_date > %s",
    369314            $hashed_auth_code, $user_id, $current_datetime
    370315        );
    371316
    372         return ($wpdb->get_var($query) > 0);
     317        $is_valid = ($wpdb->get_var($valid_token_query) > 0);
     318
     319        if (!$is_valid) {
     320            return 'expired'; // Token exists but expired
     321        }
     322
     323        return 'valid'; // Valid token
    373324    }
    374325
     
    393344        }
    394345
    395         if (true !== $this->is_valid_authcode($_REQUEST['authcode'], $user->ID)) {
     346        $authcode_validation = $this->is_valid_authcode($_REQUEST['authcode'], $user->ID);
     347
     348        if ('valid' !== $authcode_validation) {
    396349            do_action('wp_factor_telegram_failed', $user->user_login);
    397350
     
    400353            $chat_id = $this->get_user_chatid($user->ID);
    401354            $result = $this->telegram->send_tg_token($auth_code, $chat_id, $user->ID);
     355
     356            // Determine error message based on validation result
     357            $error_message = '';
     358            $log_reason = '';
     359
     360            if ($authcode_validation === 'expired') {
     361                $error_message = __('The verification code has expired. We just sent you a new code, please try again!',
     362                    'two-factor-login-telegram');
     363                $log_reason = 'expired_verification_code';
     364            } else {
     365                $error_message = __('Wrong verification code, we just sent a new code, please try again!',
     366                    'two-factor-login-telegram');
     367                $log_reason = 'wrong_verification_code';
     368            }
    402369
    403370            $this->log_telegram_action('auth_code_resent', array(
     
    406373                'chat_id' => $chat_id,
    407374                'success' => $result !== false,
    408                 'reason' => 'wrong_verification_code'
     375                'reason' => $log_reason
    409376            ));
    410377
    411             $this->login_html($user, $_REQUEST['redirect_to'],
    412                 __('Wrong verification code, we just sent a new code, please try again!',
    413                     'two-factor-login-telegram'));
     378            $this->login_html($user, $_REQUEST['redirect_to'], $error_message);
    414379            exit;
    415380        }
     
    431396    public function configure_tg()
    432397    {
    433         require(dirname(WP_FACTOR_TG_FILE) . "/sections/configure_tg.php");
     398        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/configuration.php");
    434399    }
    435400
     
    439404    public function show_telegram_logs()
    440405    {
    441         $logs = get_option('telegram_bot_logs', array());
    442 
    443         // Handle clear logs action
    444         if (isset($_POST['clear_logs']) && wp_verify_nonce($_POST['_wpnonce'], 'clear_telegram_logs')) {
    445             delete_option('telegram_bot_logs');
    446             $logs = array();
    447             echo '<div class="notice notice-success is-dismissible"><p>' . __('Logs cleared successfully.', 'two-factor-login-telegram') . '</p></div>';
    448         }
    449 
    450         ?>
    451         <div class="wrap">
    452             <h1><?php _e('Telegram Bot Logs', 'two-factor-login-telegram'); ?></h1>
    453 
    454             <form method="post">
    455                 <?php wp_nonce_field('clear_telegram_logs'); ?>
    456                 <input type="submit" name="clear_logs" class="button button-secondary" value="<?php _e('Clear Logs', 'two-factor-login-telegram'); ?>" onclick="return confirm('<?php _e('Are you sure you want to clear all logs?', 'two-factor-login-telegram'); ?>')">
    457             </form>
    458 
    459             <br>
    460 
    461             <?php if (empty($logs)): ?>
    462                 <p><?php _e('No logs available.', 'two-factor-login-telegram'); ?></p>
    463             <?php else: ?>
    464                 <table class="wp-list-table widefat fixed striped">
    465                     <thead>
    466                         <tr>
    467                             <th style="width: 15%;"><?php _e('Timestamp', 'two-factor-login-telegram'); ?></th>
    468                             <th style="width: 15%;"><?php _e('Action', 'two-factor-login-telegram'); ?></th>
    469                             <th><?php _e('Data', 'two-factor-login-telegram'); ?></th>
    470                         </tr>
    471                     </thead>
    472                     <tbody>
    473                         <?php foreach ($logs as $log): ?>
    474                             <tr>
    475                                 <td><?php echo esc_html($log['timestamp']); ?></td>
    476                                 <td><?php echo esc_html($log['action']); ?></td>
    477                                 <td>
    478                                     <details>
    479                                         <summary><?php _e('View details', 'two-factor-login-telegram'); ?></summary>
    480                                         <pre style="background: #f1f1f1; padding: 10px; margin-top: 10px; overflow-x: auto;"><?php echo esc_html(print_r($log['data'], true)); ?></pre>
    481                                     </details>
    482                                 </td>
    483                             </tr>
    484                         <?php endforeach; ?>
    485                     </tbody>
    486                 </table>
    487             <?php endif; ?>
    488         </div>
    489         <?php
     406        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/logs-page.php");
    490407    }
    491408
     
    502419    }
    503420
    504     function delete_transients($option_name, $old_value, $new_value)
    505     {
    506         if ($this->namespace === $option_name) {
    507             delete_transient(WP_FACTOR_TG_GETME_TRANSIENT);
    508 
    509             if (!empty($new_value['bot_token'])) {
    510                 $webhook_url = rest_url('telegram/v1/webhook');
    511                 $this->telegram->set_bot_token($new_value['bot_token'])->set_webhook($webhook_url);
    512             }
    513         }
     421    function sanitize_settings($input)
     422    {
     423        // Sanitize input values
     424        $sanitized = array();
     425
     426        if (isset($input['bot_token'])) {
     427            $sanitized['bot_token'] = sanitize_text_field($input['bot_token']);
     428        }
     429
     430        if (isset($input['chat_id'])) {
     431            $sanitized['chat_id'] = sanitize_text_field($input['chat_id']);
     432        }
     433
     434        if (isset($input['enabled'])) {
     435            $sanitized['enabled'] = $input['enabled'] === '1' ? '1' : '0';
     436        }
     437
     438        if (isset($input['show_site_name'])) {
     439            $sanitized['show_site_name'] = $input['show_site_name'] === '1' ? '1' : '0';
     440        }
     441
     442        if (isset($input['show_site_url'])) {
     443            $sanitized['show_site_url'] = $input['show_site_url'] === '1' ? '1' : '0';
     444        }
     445
     446        if (isset($input['delete_data_on_deactivation'])) {
     447            $sanitized['delete_data_on_deactivation'] = $input['delete_data_on_deactivation'] === '1' ? '1' : '0';
     448        }
     449
     450        // Delete transients when settings are updated
     451        delete_transient(WP_FACTOR_TG_GETME_TRANSIENT);
     452
     453        // Set webhook if bot token is provided
     454        if (!empty($sanitized['bot_token'])) {
     455            $webhook_url = rest_url('telegram/v1/webhook');
     456            $this->telegram->set_bot_token($sanitized['bot_token'])->set_webhook($webhook_url);
     457        }
     458
     459        return $sanitized;
    514460    }
    515461
    516462    function tg_register_settings()
    517463    {
    518         register_setting($this->namespace, $this->namespace);
     464        register_setting($this->namespace, $this->namespace, array(
     465            'sanitize_callback' => array($this, 'sanitize_settings')
     466        ));
    519467
    520468        add_settings_section($this->namespace . '_section',
    521469            __('Telegram Configuration', "two-factor-login-telegram"), '',
     470            $this->namespace . '.php');
     471
     472        add_settings_section($this->namespace . '_failed_login_section',
     473            __('Failed Login Report', "two-factor-login-telegram"), array($this, 'failed_login_section_callback'),
     474            $this->namespace . '.php');
     475
     476        add_settings_section($this->namespace . '_data_management_section',
     477            __('Data Management', "two-factor-login-telegram"), array($this, 'data_management_section_callback'),
    522478            $this->namespace . '.php');
    523479
     
    539495            $field_args);
    540496
     497        // Move Chat ID to Failed Login Report section
    541498        $field_args = array(
    542499            'type' => 'text',
    543500            'id' => 'chat_id',
    544501            'name' => 'chat_id',
    545             'desc' => __('Chat ID (Telegram) for failed login report.',
     502            'desc' => __('Enter your Telegram Chat ID to receive notifications about failed login attempts.',
    546503                "two-factor-login-telegram"),
    547504            'std' => '',
     
    551508
    552509        add_settings_field('chat_id',
    553             __('Chat ID', "two-factor-login-telegram"), array(
     510            __('Chat ID for Reports', "two-factor-login-telegram"), array(
    554511                $this,
    555512                'tg_display_setting',
    556             ), $this->namespace . '.php', $this->namespace . '_section',
     513            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
    557514            $field_args);
    558515
     
    575532            $field_args);
    576533
     534        // Move Show site name to Failed Login Report section
    577535        $field_args = array(
    578536            'type' => 'checkbox',
    579537            'id' => 'show_site_name',
    580538            'name' => 'show_site_name',
    581             'desc' => __('Select this checkbox to show site name on failed login attempt message.<br>This option is useful when you use same bot in several sites.',
     539            'desc' => __('Include site name in failed login notifications.<br>Useful when using the same bot for multiple sites.',
    582540                'two-factor-login-telegram'),
    583541            'std' => '',
     
    587545
    588546        add_settings_field('show_site_name',
    589             __('Show site name?', 'two-factor-login-telegram'), array(
     547            __('Show Site Name', 'two-factor-login-telegram'), array(
    590548                $this,
    591549                'tg_display_setting',
    592             ), $this->namespace . '.php', $this->namespace . '_section',
     550            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
    593551            $field_args);
    594552
     553        // Move Show site URL to Failed Login Report section
    595554        $field_args = array(
    596555            'type' => 'checkbox',
    597556            'id' => 'show_site_url',
    598557            'name' => 'show_site_url',
    599             'desc' => __('Select this checkbox to show site URL on failed login attempt message.<br>This option is useful when you use same bot in several sites.',
     558            'desc' => __('Include site URL in failed login notifications.<br>Useful when using the same bot for multiple sites.',
    600559                'two-factor-login-telegram'),
    601560            'std' => '',
     
    605564
    606565        add_settings_field('show_site_url',
    607             __('Show site URL?', 'two-factor-login-telegram'), array(
     566            __('Show Site URL', 'two-factor-login-telegram'), array(
    608567                $this,
    609568                'tg_display_setting',
    610             ), $this->namespace . '.php', $this->namespace . '_section',
     569            ), $this->namespace . '.php', $this->namespace . '_failed_login_section',
     570            $field_args);
     571
     572        // Add data cleanup option to Data Management section
     573        $field_args = array(
     574            'type' => 'checkbox',
     575            'id' => 'delete_data_on_deactivation',
     576            'name' => 'delete_data_on_deactivation',
     577            'desc' => __('Delete all plugin data when the plugin is deactivated.<br><strong>Warning:</strong> This will permanently remove all settings, user configurations, authentication codes, and logs.',
     578                'two-factor-login-telegram'),
     579            'std' => '',
     580            'label_for' => 'delete_data_on_deactivation',
     581            'class' => 'css_class',
     582        );
     583
     584        add_settings_field('delete_data_on_deactivation',
     585            __('Delete Data on Deactivation', 'two-factor-login-telegram'), array(
     586                $this,
     587                'tg_display_setting',
     588            ), $this->namespace . '.php', $this->namespace . '_data_management_section',
    611589            $field_args);
    612590    }
     
    680658
    681659    /**
     660     * Failed login section callback
     661     */
     662    public function failed_login_section_callback()
     663    {
     664        echo '<p>' . __('Configure how to receive notifications when someone fails to log in to your site.', 'two-factor-login-telegram') . '</p>';
     665    }
     666
     667    /**
     668     * Data management section callback
     669     */
     670    public function data_management_section_callback()
     671    {
     672        echo '<p>' . __('Manage plugin data and cleanup options.', 'two-factor-login-telegram') . '</p>';
     673    }
     674
     675    /**
    682676     * Action links
    683677     *
     
    735729    public function tg_add_two_factor_fields($user)
    736730    {
    737         ?>
    738         <h3 id="wptl"><?php
    739             _e('2FA with Telegram',
    740                 'two-factor-login-telegram'); ?></h3>
    741 
    742         <table class="form-table">
    743 
    744             <tr>
    745                 <th>
    746                     <label for="tg_wp_factor_enabled"><?php
    747                         _e('Enable 2FA',
    748                             'two-factor-login-telegram'); ?>
    749                     </label>
    750                 </th>
    751                 <td colspan="2">
    752                     <input type="hidden" name="tg_wp_factor_valid"
    753                            id="tg_wp_factor_valid" value="<?php
    754                     echo (int)(esc_attr(get_the_author_meta('tg_wp_factor_enabled',
    755                             $user->ID)) === "1"); ?>">
    756                     <input type="checkbox" name="tg_wp_factor_enabled"
    757                            id="tg_wp_factor_enabled" value="1"
    758                            class="regular-text" <?php
    759                     echo checked(esc_attr(get_the_author_meta('tg_wp_factor_enabled',
    760                         $user->ID)), 1); ?> /><br/>
    761                 </td>
    762             </tr>
    763 
    764             <tr>
    765 
    766                 <td colspan="3">
    767 
    768                     <?php
    769                     $username = $this->telegram->get_me()->username;
    770                     ?>
    771 
    772                     <div>
    773 
    774                         <ol>
    775                             <li>
    776                                 <?php
    777                                 printf(__('Open a conversation with %s and press on <strong>Start</strong>',
    778                                     'two-factor-login-telegram'),
    779                                     '<a href="https://telegram.me/' . $username
    780                                     . '" target="_blank">@' . $username . '</a>');
    781                                 ?>
    782                             </li>
    783 
    784                             <li>
    785                                 <?php
    786                                 printf(__('Type command %s to obtain your Chat ID.',
    787                                     "two-factor-login-telegram"),
    788                                     '<code>/get_id</code>');
    789                                 ?>
    790                             </li>
    791                             <li>
    792                                 <?php
    793                                 _e("The bot will reply with your <strong>Chat ID</strong> number",
    794                                     'two-factor-login-telegram');
    795                                 ?>
    796                             </li>
    797 
    798                             <li><?php
    799                                 _e('Copy your Chat ID and paste it below, then press <strong>Submit code</strong>',
    800                                     'two-factor-login-telegram'); ?></li>
    801                         </ol>
    802                     </div>
    803                 </td>
    804 
    805             </tr>
    806 
    807             <tr>
    808                 <th>
    809                     <label for="tg_wp_factor_chat_id"><?php
    810                         _e('Telegram Chat ID',
    811                             'two-factor-login-telegram'); ?>
    812                     </label></th>
    813                 <td>
    814                     <input type="text" name="tg_wp_factor_chat_id"
    815                            id="tg_wp_factor_chat_id" value="<?php
    816                     echo esc_attr(get_the_author_meta('tg_wp_factor_chat_id',
    817                         $user->ID)); ?>" class="regular-text"/><br/>
    818                     <span class="description"><?php
    819                         _e('Put your Telegram Chat ID',
    820                             'two-factor-login-telegram'); ?></span>
    821                 </td>
    822                 <td>
    823                     <button class="button" id="tg_wp_factor_chat_id_send"><?php
    824                         _e("Submit code",
    825                             "two-factor-login-telegram"); ?></button>
    826                 </td>
    827             </tr>
    828 
    829             <tr id="factor-chat-confirm">
    830                 <th>
    831                     <label for="tg_wp_factor_chat_id_confirm"><?php
    832                         _e('Confirmation code',
    833                             'two-factor-login-telegram'); ?>
    834                     </label></th>
    835                 <td>
    836                     <input type="text" name="tg_wp_factor_chat_id_confirm"
    837                            id="tg_wp_factor_chat_id_confirm" value=""
    838                            class="regular-text"/><br/>
    839                     <span class="description"><?php
    840                         _e('Please enter the confirmation code you received on Telegram',
    841                             'two-factor-login-telegram'); ?></span>
    842                 </td>
    843                 <td>
    844                     <button class="button" id="tg_wp_factor_chat_id_check"><?php
    845                         _e("Validate",
    846                             "two-factor-login-telegram"); ?></button>
    847                 </td>
    848             </tr>
    849             <tr id="factor-chat-response">
    850                 <td colspan="3">
    851                     <div class="wpft-notice wpft-notice-warning">
    852                         <p></p>
    853                     </div>
    854                 </td>
    855             </tr>
    856         </table>
    857         <?php
     731        require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/user-2fa-form.php");
    858732    }
    859733
     
    878752                "sendtoken_nonce" => wp_create_nonce('ajax-sendtoken-nonce'),
    879753                "tokencheck_nonce" => wp_create_nonce('ajax-tokencheck-nonce'),
    880                 "spinner" => admin_url("/images/spinner.gif")
     754                "spinner" => admin_url("/images/spinner.gif"),
     755                // Translated messages
     756                "invalid_chat_id" => __('Please enter a valid Chat ID', 'two-factor-login-telegram'),
     757                "enter_confirmation_code" => __('Please enter the confirmation code', 'two-factor-login-telegram'),
     758                "setup_completed" => __('✅ 2FA setup completed successfully!', 'two-factor-login-telegram'),
     759                "code_sent" => __('✅ Code sent! Check your Telegram', 'two-factor-login-telegram'),
     760                "modifying_setup" => __('⚠️ Modifying 2FA configuration - validation required', 'two-factor-login-telegram')
    881761            ));
    882762
     
    954834            __("Use this code to complete your 2FA setup in WordPress, or click the button below:", "two-factor-login-telegram")
    955835        );
    956        
     836
    957837        // Create inline keyboard with validation button
    958838        $reply_markup = null;
    959839        if ($current_user_id) {
    960840            $nonce = wp_create_nonce('telegram_validate_' . $current_user_id . '_' . $auth_code);
    961             $validation_url = admin_url('admin.php?page=tg-conf&action=telegram_validate&user_id=' . $current_user_id . '&token=' . $auth_code . '&nonce=' . $nonce);
    962            
     841            $validation_url = admin_url('options-general.php?page=tg-conf&action=telegram_validate&chat_id='.$_POST['chat_id'].'&user_id=' . $current_user_id . '&token=' . $auth_code . '&nonce=' . $nonce);
     842
    963843            $reply_markup = array(
    964844                'inline_keyboard' => array(
     
    972852            );
    973853        }
    974        
     854
    975855        $send = $tg->send_with_keyboard($validation_message, $_POST['chat_id'], $reply_markup);
    976856
     
    1077957    }
    1078958
    1079     public function tg_save_custom_user_profile_fields($user_id)
     959    /**
     960     * Save user's 2FA settings
     961     *
     962     * @param int $user_id User ID
     963     * @param string $chat_id Telegram Chat ID
     964     * @param bool $enabled Whether 2FA is enabled
     965     * @return bool Success status
     966     */
     967    public function save_user_2fa_settings($user_id, $chat_id, $enabled = true)
    1080968    {
    1081969        if (!current_user_can('edit_user', $user_id)) {
     
    1083971        }
    1084972
     973        if (empty($chat_id)) {
     974            return false;
     975        }
     976
     977        update_user_meta($user_id, 'tg_wp_factor_chat_id', sanitize_text_field($chat_id));
     978        update_user_meta($user_id, 'tg_wp_factor_enabled', $enabled ? '1' : '0');
     979
     980        return true;
     981    }
     982
     983    public function tg_save_custom_user_profile_fields($user_id)
     984    {
    1085985        if ($_POST['tg_wp_factor_valid'] == 0
    1086986            || $_POST['tg_wp_factor_chat_id'] == ""
     
    1089989        }
    1090990
    1091         update_user_meta($user_id, 'tg_wp_factor_chat_id',
    1092             $_POST['tg_wp_factor_chat_id']);
    1093         update_user_meta($user_id, 'tg_wp_factor_enabled',
    1094             $_POST['tg_wp_factor_enabled']);
    1095 
    1096         return true;
     991        return $this->save_user_2fa_settings(
     992            $user_id,
     993            $_POST['tg_wp_factor_chat_id'],
     994            isset($_POST['tg_wp_factor_enabled'])
     995        );
    1097996    }
    1098997
     
    11691068    }
    11701069
     1070    private function create_or_update_activities_table()
     1071    {
     1072        global $wpdb;
     1073
     1074        $table_name = $wpdb->prefix . 'wp2fat_activities';
     1075
     1076        $charset_collate = $wpdb->get_charset_collate();
     1077        $sql = "CREATE TABLE IF NOT EXISTS $table_name (
     1078        id mediumint(9) NOT NULL AUTO_INCREMENT, 
     1079        timestamp datetime NOT NULL,
     1080        action varchar(100) NOT NULL,
     1081        data longtext,
     1082        PRIMARY KEY (id),
     1083        KEY timestamp (timestamp),
     1084        KEY action (action)
     1085    ) $charset_collate";
     1086
     1087        require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
     1088
     1089        // Esegue la query per creare/aggiornare la tabella
     1090        dbDelta($sql);
     1091    }
     1092
     1093    private function migrate_logs_to_activities_table()
     1094    {
     1095        global $wpdb;
     1096
     1097        // Check if old logs exist in option
     1098        $old_logs = get_option('telegram_bot_logs', array());
     1099
     1100        if (!empty($old_logs)) {
     1101            $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1102
     1103            // Migrate each log entry
     1104            foreach ($old_logs as $log) {
     1105                $wpdb->insert(
     1106                    $activities_table,
     1107                    array(
     1108                        'timestamp' => $log['timestamp'],
     1109                        'action' => $log['action'],
     1110                        'data' => maybe_serialize($log['data'])
     1111                    ),
     1112                    array('%s', '%s', '%s')
     1113                );
     1114            }
     1115
     1116            // Delete the old option after migration
     1117            delete_option('telegram_bot_logs');
     1118        }
     1119    }
     1120
    11711121    function plugin_activation()
    11721122    {
    11731123        $this->create_or_update_telegram_auth_codes_table();
     1124        $this->create_or_update_activities_table();
     1125        $this->migrate_logs_to_activities_table();
    11741126        update_option('wp_factor_plugin_version', WP_FACTOR_PLUGIN_VERSION);
    11751127
    1176         // Flush rewrite rules to add our webhook endpoint
     1128        // Add rewrite rules before flushing
     1129        $this->add_telegram_rewrite_rules();
     1130
     1131        // Flush rewrite rules to add our new rules
    11771132        flush_rewrite_rules();
     1133    }
     1134
     1135    function plugin_deactivation()
     1136    {
     1137        // Check if data cleanup is enabled
     1138        $plugin_options = get_option($this->namespace, array());
     1139
     1140        if (isset($plugin_options['delete_data_on_deactivation']) && $plugin_options['delete_data_on_deactivation'] === '1') {
     1141            $this->cleanup_all_plugin_data();
     1142        }
     1143
     1144        // Always flush rewrite rules on deactivation
     1145        flush_rewrite_rules();
     1146    }
     1147
     1148    private function cleanup_all_plugin_data()
     1149    {
     1150        global $wpdb;
     1151
     1152        // Delete plugin settings
     1153        delete_option($this->namespace);
     1154        delete_option('wp_factor_plugin_version');
     1155        delete_option('telegram_bot_logs'); // For backward compatibility
     1156
     1157        // Delete auth codes table
     1158        $auth_codes_table = $wpdb->prefix . 'telegram_auth_codes';
     1159        $wpdb->query("DROP TABLE IF EXISTS $auth_codes_table");
     1160
     1161        // Delete activities table
     1162        $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1163        $wpdb->query("DROP TABLE IF EXISTS $activities_table");
     1164
     1165        // Delete user meta data for all users
     1166        $wpdb->delete(
     1167            $wpdb->usermeta,
     1168            array(
     1169                'meta_key' => 'tg_wp_factor_chat_id'
     1170            )
     1171        );
     1172
     1173        $wpdb->delete(
     1174            $wpdb->usermeta,
     1175            array(
     1176                'meta_key' => 'tg_wp_factor_enabled'
     1177            )
     1178        );
     1179
     1180        // Delete all transients related to the plugin
     1181        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_wp2fa_telegram_authcode_%'");
     1182        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_wp2fa_telegram_authcode_%'");
     1183        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_" . WP_FACTOR_TG_GETME_TRANSIENT . "%'");
     1184        $wpdb->query("DELETE FROM $wpdb->options WHERE option_name LIKE '_transient_timeout_" . WP_FACTOR_TG_GETME_TRANSIENT . "%'");
    11781185    }
    11791186
     
    11841191        if ($installed_version !== WP_FACTOR_PLUGIN_VERSION) {
    11851192            $this->create_or_update_telegram_auth_codes_table();
     1193            $this->create_or_update_activities_table();
     1194            $this->migrate_logs_to_activities_table();
    11861195            update_option('wp_factor_plugin_version', WP_FACTOR_PLUGIN_VERSION);
    11871196        }
     
    12011210
    12021211        register_activation_hook(WP_FACTOR_TG_FILE, array($this, 'plugin_activation'));
     1212        register_deactivation_hook(WP_FACTOR_TG_FILE, array($this, 'plugin_deactivation'));
    12031213        add_action('plugins_loaded', array($this, 'check_plugin_update'));
    12041214
     
    12091219                . plugin_basename(WP_FACTOR_TG_FILE),
    12101220                array($this, 'action_links'));
    1211             add_action('updated_option', array($this, 'delete_transients'),
    1212                 10, 3);
    12131221        }
    12141222
     
    12231231        }
    12241232
    1225         add_action('show_user_profile',
    1226             array($this, 'tg_add_two_factor_fields'));
    1227         add_action('edit_user_profile',
    1228             array($this, 'tg_add_two_factor_fields'));
     1233        if ($this->is_valid_bot()) {
     1234            add_action('show_user_profile',
     1235                array($this, 'tg_add_two_factor_fields'));
     1236            add_action('edit_user_profile',
     1237                array($this, 'tg_add_two_factor_fields'));
     1238        }
    12291239
    12301240        add_action('personal_options_update',
     
    12421252        // Add REST API endpoint for Telegram webhook
    12431253        add_action('rest_api_init', array($this, 'register_telegram_webhook_route'));
     1254
     1255        // Add rewrite rules for Telegram confirmation URLs
     1256        add_action('init', array($this, 'add_telegram_rewrite_rules'));
     1257        add_action('parse_request', array($this, 'parse_telegram_request'));
     1258
    12441259    }
    12451260
     
    12531268            'permission_callback' => '__return_true',
    12541269        ));
    1255 
    1256         register_rest_route('telegram/v1', '/confirm/(?P<user_id>\d+)/(?P<token>[a-zA-Z0-9]+)', array(
    1257             'methods' => 'GET',
    1258             'callback' => array($this, 'handle_telegram_confirmation'),
    1259             'permission_callback' => '__return_true',
    1260             'args' => array(
    1261                 'user_id' => array(
    1262                     'validate_callback' => function($param, $request, $key) {
    1263                         return is_numeric($param);
    1264                     }
    1265                 ),
    1266                 'token' => array(
    1267                     'validate_callback' => function($param, $request, $key) {
    1268                         return preg_match('/^[a-zA-Z0-9]+$/', $param);
    1269                     }
    1270                 ),
    1271                 'nonce' => array(
    1272                     'required' => true,
    1273                 )
     1270    }
     1271
     1272    /**
     1273     * Add rewrite rules for Telegram confirmation URLs
     1274     */
     1275    public function add_telegram_rewrite_rules() {
     1276        add_rewrite_rule(
     1277            '^telegram-confirm/([0-9]+)/([a-zA-Z0-9]+)/?$',
     1278            'index.php?telegram_confirm=1&user_id=$matches[1]&token=$matches[2]',
     1279            'top'
     1280        );
     1281
     1282        // Add query vars
     1283        add_filter('query_vars', function($vars) {
     1284            $vars[] = 'telegram_confirm';
     1285            $vars[] = 'user_id';
     1286            $vars[] = 'token';
     1287            return $vars;
     1288        });
     1289    }
     1290
     1291    /**
     1292     * Parse Telegram confirmation requests
     1293     */
     1294    public function parse_telegram_request() {
     1295        global $wp;
     1296
     1297        if (isset($wp->query_vars['telegram_confirm']) && $wp->query_vars['telegram_confirm'] == 1) {
     1298            $user_id = intval($wp->query_vars['user_id']);
     1299            $token = sanitize_text_field($wp->query_vars['token']);
     1300            $nonce = sanitize_text_field($_GET['nonce'] ?? '');
     1301
     1302            $this->handle_telegram_confirmation_direct($user_id, $token, $nonce);
     1303        }
     1304    }
     1305
     1306    /**
     1307     * Handle Telegram confirmation via direct URL (not REST API)
     1308     */
     1309    public function handle_telegram_confirmation_direct($user_id, $token, $nonce) {
     1310        // Verify nonce
     1311        if (!wp_verify_nonce($nonce, 'telegram_confirm_' . $user_id . '_' . $token)) {
     1312            $this->log_telegram_action('confirmation_failed', array(
     1313                'user_id' => $user_id,
     1314                'token' => $token,
     1315                'reason' => 'invalid_nonce'
     1316            ));
     1317
     1318            // Include error template
     1319            require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-security-failed.php");
     1320        }
     1321
     1322        // Validate the token
     1323        $authcode_validation = $this->is_valid_authcode($token, $user_id);
     1324
     1325        if ('valid' !== $authcode_validation) {
     1326            if ($authcode_validation === 'expired') {
     1327                $log_reason = 'expired_token';
     1328            } else {
     1329                $log_reason = 'invalid_token';
     1330            }
     1331
     1332            $this->log_telegram_action('confirmation_failed', array(
     1333                'user_id' => $user_id,
     1334                'token' => $token,
     1335                'reason' => $log_reason
     1336            ));
     1337
     1338            // Include appropriate error template
     1339            if ($authcode_validation === 'expired') {
     1340                require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-expired-token.php");
     1341            } else {
     1342                require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-invalid-token.php");
     1343            }
     1344        }
     1345
     1346        // Get user
     1347        $user = get_userdata($user_id);
     1348        if (!$user) {
     1349            require_once(dirname(WP_FACTOR_TG_FILE) . "/templates/error-invalid-token.php");
     1350        }
     1351
     1352        // Log the user in
     1353        wp_set_auth_cookie($user_id, false);
     1354
     1355        $this->log_telegram_action('confirmation_success', array(
     1356            'user_id' => $user_id,
     1357            'user_login' => $user->user_login,
     1358            'token' => $token,
     1359            'method' => 'direct_confirmation'
     1360        ));
     1361
     1362        // Redirect to admin or specified location
     1363        $redirect_url = apply_filters('telegram_confirmation_redirect_url', admin_url(), $user);
     1364
     1365        // Redirect directly
     1366        wp_safe_redirect($redirect_url);
     1367        exit;
     1368    }
     1369
     1370    /**
     1371     * Log Telegram bot actions
     1372     */
     1373    public function log_telegram_action($action, $data = array()) {
     1374        global $wpdb;
     1375
     1376        $activities_table = $wpdb->prefix . 'wp2fat_activities';
     1377
     1378        // Insert new log entry
     1379        $wpdb->insert(
     1380            $activities_table,
     1381            array(
     1382                'timestamp' => current_time('mysql'),
     1383                'action' => $action,
     1384                'data' => maybe_serialize($data)
    12741385            ),
    1275         ));
    1276     }
    1277 
    1278     /**
    1279      * Log Telegram bot actions
    1280      */
    1281     private function log_telegram_action($action, $data = array()) {
    1282         $log_entry = array(
    1283             'timestamp' => current_time('mysql'),
    1284             'action' => $action,
    1285             'data' => $data
    1286         );
    1287 
    1288         $logs = get_option('telegram_bot_logs', array());
    1289         array_unshift($logs, $log_entry);
    1290 
    1291         // Keep only last 100 log entries
    1292         $logs = array_slice($logs, 0, 100);
    1293 
    1294         update_option('telegram_bot_logs', $logs);
     1386            array('%s', '%s', '%s')
     1387        );
     1388
     1389        // Clean up old entries - keep only last 1000 entries
     1390        $wpdb->query("DELETE FROM $activities_table WHERE id NOT IN (SELECT id FROM (SELECT id FROM $activities_table ORDER BY timestamp DESC LIMIT 1000) temp_table)");
    12951391    }
    12961392
     
    13511447    }
    13521448
    1353     /**
    1354      * Handle Telegram confirmation via link
    1355      */
    1356     public function handle_telegram_confirmation($request) {
    1357         $user_id = intval($request['user_id']);
    1358         $token = sanitize_text_field($request['token']);
    1359         $nonce = sanitize_text_field($request->get_param('nonce'));
    1360        
    1361         // Verify nonce
    1362         if (!wp_verify_nonce($nonce, 'telegram_confirm_' . $user_id . '_' . $token)) {
    1363             $this->log_telegram_action('confirmation_failed', array(
    1364                 'user_id' => $user_id,
    1365                 'token' => $token,
    1366                 'reason' => 'invalid_nonce'
    1367             ));
    1368             return new WP_Error('invalid_nonce', __('Security check failed.', 'two-factor-login-telegram'), array('status' => 403));
    1369         }
    1370        
    1371         // Validate the token
    1372         if (!$this->is_valid_authcode($token, $user_id)) {
    1373             $this->log_telegram_action('confirmation_failed', array(
    1374                 'user_id' => $user_id,
    1375                 'token' => $token,
    1376                 'reason' => 'invalid_token'
    1377             ));
    1378             return new WP_Error('invalid_token', __('Invalid or expired token.', 'two-factor-login-telegram'), array('status' => 400));
    1379         }
    1380        
    1381         // Get user
    1382         $user = get_userdata($user_id);
    1383         if (!$user) {
    1384             return new WP_Error('invalid_user', __('Invalid user.', 'two-factor-login-telegram'), array('status' => 400));
    1385         }
    1386        
    1387         // Log the user in
    1388         wp_set_auth_cookie($user_id, false);
    1389        
    1390         $this->log_telegram_action('confirmation_success', array(
    1391             'user_id' => $user_id,
    1392             'user_login' => $user->user_login,
    1393             'token' => $token,
    1394             'method' => 'link_confirmation'
    1395         ));
    1396        
    1397         // Redirect to admin or specified location
    1398         $redirect_url = apply_filters('telegram_confirmation_redirect_url', admin_url(), $user);
    1399        
    1400         // Redirect directly instead of returning JSON
    1401         wp_safe_redirect($redirect_url);
    1402         exit;
    1403     }
     1449
    14041450
    14051451}
  • two-factor-login-telegram/trunk/includes/class-wp-telegram.php

    r3331421 r3332295  
    148148        if ($user_id) {
    149149            $nonce = wp_create_nonce('telegram_confirm_' . $user_id . '_' . $token);
    150             $confirmation_url = site_url('/wp-json/telegram/v1/confirm/' . $user_id . '/' . $token . '?nonce=' . $nonce);
     150            $confirmation_url = home_url('/telegram-confirm/' . $user_id . '/' . $token . '/?nonce=' . $nonce);
    151151           
    152152            $reply_markup = array(
  • two-factor-login-telegram/trunk/two-factor-telegram.php

    r3331421 r3332295  
    55 * Plugin URI: https://blog.dueclic.com/wordpress-autenticazione-due-fattori-telegram/
    66 * Description: This plugin enables two factor authentication with Telegram by increasing your website security and sends an alert every time a wrong login occurs.
    7  * Version: 3.4
     7 * Version: 3.5.0
    88 * Requires at least: 6.0
    99 * Tested up to: 6.8
     
    2222}
    2323
    24 define('WP_FACTOR_PLUGIN_VERSION', '3.4');
     24define('WP_FACTOR_PLUGIN_VERSION', '3.5.0');
    2525
    2626define('WP_FACTOR_AUTHCODE_EXPIRE_SECONDS', 60 * 20);
Note: See TracChangeset for help on using the changeset viewer.