Plugin Directory

Changeset 3439073


Ignore:
Timestamp:
01/14/2026 12:42:59 AM (5 hours ago)
Author:
websitetoolbox
Message:

Adds logging

Location:
website-toolbox-forums
Files:
32 added
10 edited

Legend:

Unmodified
Added
Removed
  • website-toolbox-forums/trunk/admin/admin.php

    r3395282 r3439073  
    44 */
    55namespace WebsiteToolboxAdmin;
     6
     7// If this file is called directly, abort.
     8if ( ! defined( 'ABSPATH' ) ) {
     9    exit;
     10}
     11
    612use WebsiteToolboxForum;
    713use WebsiteToolboxForum\globalVariables;
     
    160166            );
    161167            wp_update_post($wtbPostData);
     168           
     169            // Log embed page deactivation
     170            \WebsiteToolboxLogger\Logger::info('PAGE_DEACTIVATE', 'Embed page set to draft during plugin deactivation', array(
     171                'page_id' => $websitetoolboxpage_id,
     172                'page_title' => $wtbPost->post_title,
     173                'previous_status' => $wtbPost->post_status
     174            ));
    162175        }
    163176    }
     
    165178    if(get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_username")){
    166179        httpRequestForUninstallReinstallPlugin('uninstallPlugin');
    167     }   
     180    }
    168181}
    169182
     
    183196        $my_post['post_title']  = WebsiteToolboxForum\globalVariables::$PAGENAME;
    184197    }
     198   
     199    // Check if a page with the same title already exists (before creating new one)
     200    if ($my_post['post_title'] == WebsiteToolboxForum\globalVariables::$PAGENAME) {
     201        // Use WP_Query for exact title matching (replaces deprecated get_page_by_title)
     202        $query = new \WP_Query(array(
     203            'post_type' => 'page',
     204            'title' => $my_post['post_title'],
     205            'posts_per_page' => 1,
     206            'no_found_rows' => true,
     207            'ignore_sticky_posts' => true,
     208            'fields' => 'ids'
     209        ));
     210       
     211        $existingPage = null;
     212        if ($query->have_posts()) {
     213            $pageId = $query->posts[0];
     214            $existingPage = get_post($pageId);
     215        }
     216        wp_reset_postdata();
     217       
     218        if ($existingPage && $existingPage->ID != $forumPostId) {
     219            // Log when another page with the same title exists
     220            \WebsiteToolboxLogger\Logger::warning('PAGE_DUPLICATE', 'There is another page that exists with the same title "' . $my_post['post_title'] . '" on the website', array(
     221                'existing_page_id' => $existingPage->ID,
     222                'existing_page_title' => $existingPage->post_title,
     223                'existing_page_url' => get_permalink($existingPage->ID),
     224                'existing_page_status' => $existingPage->post_status,
     225                'new_page_title' => $my_post['post_title']
     226            ));
     227        }
     228    }
     229   
    185230    $my_post['post_content']    = "Please go to the admin section and change your Website Toolbox Forum settings.";
    186231    $my_post['post_status']     = 'publish';
     
    191236    $my_post['ping_status']     = 'closed';
    192237    $postId = wp_insert_post( $my_post );
     238   
     239    // Log new embed page creation
     240    if ($postId && !is_wp_error($postId)) {
     241        $logContext = array(
     242            'page_id' => $postId,
     243            'page_title' => $my_post['post_title'],
     244            'created_by' => 'plugin',
     245            'page_link' => get_permalink($postId)
     246        );
     247       
     248        // Check if another page with "Forum" title exists after creation
     249        if ($my_post['post_title'] == WebsiteToolboxForum\globalVariables::$PAGENAME) {
     250            // Use WP_Query for exact title matching (replaces deprecated get_page_by_title)
     251            $query = new \WP_Query(array(
     252                'post_type' => 'page',
     253                'title' => $my_post['post_title'],
     254                'posts_per_page' => 1,
     255                'no_found_rows' => true,
     256                'ignore_sticky_posts' => true,
     257                'fields' => 'ids'
     258            ));
     259           
     260            $existingPage = null;
     261            if ($query->have_posts()) {
     262                $pageId = $query->posts[0];
     263                $existingPage = get_post($pageId);
     264            }
     265            wp_reset_postdata();
     266           
     267            // Check if found page is different from the one we just created
     268            if ($existingPage && $existingPage->ID != $postId) {
     269                $logContext['message'] = '"/forum" link already exists on the website';
     270            }
     271        }
     272       
     273        \WebsiteToolboxLogger\Logger::info('PAGE_CREATE', 'New embed page created via plugin', $logContext);
     274    } else {
     275        \WebsiteToolboxLogger\Logger::error('PAGE_CREATE', 'Failed to create embed page via plugin', \WebsiteToolboxLogger\Logger::sanitizeLogData(array(
     276            'error' => is_wp_error($postId) ? $postId->get_error_message() : 'Unknown error'
     277        )));
     278    }
     279   
    193280    return $postId;
    194281}
     
    198285Return: None */
    199286function showForumDashboardMenu() {
    200     $wtbMenuName = 'Website Toolbox Forum';
     287    $iconUrl = 'data:image/svg+xml;base64,PHN2ZyBpZD0ibG9nb18xXyIgZGF0YS1uYW1lPSJsb2dvICgxKSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMjMuMDI2IiBoZWlnaHQ9IjIzLjAyMyIgdmlld0JveD0iMCAwIDIzLjAyNiAyMy4wMjMiPgogIDxnIGlkPSJHcm91cF84MCIgZGF0YS1uYW1lPSJHcm91cCA4MCI+CiAgICA8ZyBpZD0iR3JvdXBfNzkiIGRhdGEtbmFtZT0iR3JvdXAgNzkiPgogICAgICA8cGF0aCBpZD0iUGF0aF82NiIgZGF0YS1uYW1lPSJQYXRoIDY2IiBkPSJNMjMuMDI0LDEzLjJhLjU3Ni41NzYsMCwwLDEtLjQyLjU0bC0yLjc3My40MmE4LjQ4OCw4LjQ4OCwwLDAsMS0uNTg1LDEuMzY0Yy41MS43MzQsMS4wNDksMS4zOTQsMS42LDIuMDY4YS41OTIuNTkyLDAsMCwxLC4xNS4zNzUuNS41LDAsMCwxLS4xMzUuMzQ1QzIwLjUsMTguOCwxOC40ODIsMjEsMTcuOTcyLDIxYS43NjMuNzYzLDAsMCwxLS4zOS0uMTM1bC0yLjA2OC0xLjYxOWE5LjAzNCw5LjAzNCwwLDAsMS0xLjM2NC41NywxOS41ODQsMTkuNTg0LDAsMCwxLS40MzUsMi43ODguNTUuNTUsMCwwLDEtLjU0LjQySDkuODQ4YS41MzcuNTM3LDAsMCwxLS41NC0uNDVsLS40Mi0yLjc1OGE4Ljg5Myw4Ljg5MywwLDAsMS0xLjM0OS0uNTU0bC0yLjExNCwxLjZBLjU3Ni41NzYsMCwwLDEsNS4wNSwyMWEuNTM3LjUzNywwLDAsMS0uMzc1LS4xNjVBMTguNjc1LDE4LjY3NSwwLDAsMSwyLjIsMTguMzE4YS41ODQuNTg0LDAsMCwxLS4xMDUtLjM0NS41NjYuNTY2LDAsMCwxLC4xMi0uMzQ1Yy41MS0uNjg5LDEuMDY0LTEuMzQ5LDEuNTc0LTIuMDUzYTcuOTQ5LDcuOTQ5LDAsMCwxLS42MTUtMS40ODRsLTIuNzQzLS40QS41NDkuNTQ5LDAsMCwxLDAsMTMuMTQ1VjkuODE4YS41ODEuNTgxLDAsMCwxLC40MDUtLjU0bDIuNzg4LS40MmE3LjkyNyw3LjkyNywwLDAsMSwuNTg1LTEuMzc5Yy0uNTEtLjcxOS0xLjA1LTEuMzk0LTEuNi0yLjA2OWEuNTU0LjU1NCwwLDAsMS0uMTUtLjM2QS41ODIuNTgyLDAsMCwxLDIuMTU5LDQuN2MuMzYtLjQ5NSwyLjM4My0yLjY4MywyLjg5My0yLjY4M2EuNjY3LjY2NywwLDAsMSwuMzkuMTVMNy41MSwzLjc3N2E5LjAzNCw5LjAzNCwwLDAsMSwxLjM2NC0uNTdBMTkuNTg0LDE5LjU4NCwwLDAsMSw5LjMwOS40MTkuNTQ3LjU0NywwLDAsMSw5Ljg0OCwwaDMuMzI4YS41MzcuNTM3LDAsMCwxLC41NC40NWwuNDIsMi43NThhOC44OTMsOC44OTMsMCwwLDEsMS4zNDkuNTU0bDIuMTI5LTEuNmEuNTExLjUxMSwwLDAsMSwuMzYtLjEzNS41OTIuNTkyLDAsMCwxLC4zNzUuMTUsMTguOSwxOC45LDAsMCwxLDIuNDczLDIuNTQ4LjUxMS41MTEsMCwwLDEsLjEuMzMuNTY2LjU2NiwwLDAsMS0uMTIuMzQ1Yy0uNTEuNjg5LTEuMDY0LDEuMzQ5LTEuNTc0LDIuMDUzYTguMjU0LDguMjU0LDAsMCwxLC42MTUsMS40NjlsMi43NDMuNDJhLjU0Ny41NDcsMCwwLDEsLjQzNS41NFYxMy4yWk0xMS41MTIsNy42NzRhMy44MzcsMy44MzcsMCwxLDAsMy44MzcsMy44MzdBMy44NDUsMy44NDUsMCwwLDAsMTEuNTEyLDcuNjc0WiIgZmlsbD0iY3VycmVudENvbG9yIi8+CiAgICA8L2c+CiAgPC9nPgo8L3N2Zz4K';
     288   
    201289    if(get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_username')) {
    202         add_options_page( 'Website Toolbox', $wtbMenuName, 'manage_options', WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxUpdateOptions', 'WebsiteToolboxAdmin\\onForumUpdateSettings');
    203         $wtbMenuName = '';
    204     }
    205     add_options_page( 'Website Toolbox', $wtbMenuName, 'manage_options', WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxoptions', 'WebsiteToolboxAdmin\\onForumDashboardLogin');
     290        // Add top-level menu
     291        add_menu_page(
     292            'Website Toolbox Forum',           // Page title
     293            'Website Toolbox',                 // Menu title
     294            'manage_options',                  // Capability
     295            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxUpdateOptions',  // Menu slug
     296            'WebsiteToolboxAdmin\\onForumUpdateSettings',  // Callback function
     297            $iconUrl,                          // Icon URL
     298            81
     299        );
     300       
     301        add_submenu_page(
     302            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxUpdateOptions',  // Parent slug
     303            'Website Toolbox Forum Settings',  // Page title
     304            'Settings',                        // Menu title
     305            'manage_options',                  // Capability
     306            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxUpdateOptions',  // Menu slug (same as parent)
     307            'WebsiteToolboxAdmin\\onForumUpdateSettings'   // Callback function
     308        );
     309    } else {
     310        add_menu_page(
     311            'Website Toolbox Forum',           // Page title
     312            'Website Toolbox',                 // Menu title
     313            'manage_options',                  // Capability
     314            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxoptions',  // Menu slug
     315            'WebsiteToolboxAdmin\\onForumDashboardLogin',  // Callback function
     316            $iconUrl,                          // Icon URL
     317            81
     318        );
     319       
     320        // Rename the first submenu item to "Settings"
     321        add_submenu_page(
     322            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxoptions',  // Parent slug
     323            'Website Toolbox Forum',           // Page title
     324            'Settings',                        // Menu title
     325            'manage_options',                  // Capability
     326            WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxoptions',  // Menu slug (same as parent)
     327            'WebsiteToolboxAdmin\\onForumDashboardLogin'   // Callback function
     328        );
     329    }
    206330}
    207331
     
    286410    $userRoles = array();
    287411    if (!isset($wp_roles)){
    288         $wp_roles = new WP_Roles();
     412        $wp_roles = new \WP_Roles();
    289413    }
    290414    $roles          = $wp_roles->get_names();
     
    363487Return: Nothing */
    364488function forumSettingsPage($message = "") {
     489    // Determine the correct page to submit to based on login status
     490    $isLoggedIn = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_username');
     491    $submitPage = $isLoggedIn ? 'websitetoolboxUpdateOptions&change=1' : 'websitetoolboxoptions';
     492   
     493    // Build form action URL properly using WordPress functions
     494    $formAction = admin_url('admin.php?page=' . WebsiteToolboxForum\globalVariables::$WTBPREFIX . $submitPage);
    365495    ?>
    366496    <!-- create a form in the wordpress admin panel -->
    367497    <div class="wrap">
    368         <form name="form_lol" action="options-general.php?page=<?php echo esc_html((WebsiteToolboxForum\globalVariables::$WTBPREFIX)); ?>websitetoolboxoptions" method="POST" onsubmit="return ValidateForm();" class="custom-login-page"><?php
     498        <form name="form_lol" action="<?php echo esc_url($formAction); ?>" method="POST" onsubmit="return ValidateForm();" class="custom-login-page"><?php
    369499            dashboardLoginDescription();
    370500            if($message == 'username' || $message == 'password') {
    371501                echo wp_kses("<div id='wtb-warning' class='error'  style='margin-top:20px !important;'><p>Please enter your Website Toolbox $message. </p></div>", WebsiteToolboxInclude\wtbGetAllowedHTMLTags());
    372             } else if($message && $message != 'success') {
     502            } elseif($message && $message != 'success') {
    373503                $message = rtrim($message,'.');
    374504                if(strpos($message,'username')){
     
    463593                $wtbExist = 0;
    464594                $wtbForumpageid = createOrUpdateForumPage();
    465             } else if(!empty($wtbForumpageid) && $pageStatus !== 'publish') {
     595            } elseif(!empty($wtbForumpageid) && $pageStatus !== 'publish') {
    466596                onForumPluginActivation('settings');
    467597            }
     
    480610            if($pageData && is_object($pageData) && isset($pageData->post_type) && $pageData->post_type == 'nav_menu_item') {
    481611                $wtbOptions = array_merge($wtbOptions, array(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_redirect"=>''));
     612               
     613                // Log when SSO is set up with existing custom link/menu item
     614                \WebsiteToolboxLogger\Logger::info('PAGE_CONNECT', 'SSO configured with existing menu link', array(
     615                    'page_id' => $wtbForumpageid,
     616                    'post_type' => 'nav_menu_item',
     617                    'page_title' => $pageData->post_title,
     618                    'link_type' => 'custom_link',
     619                    'setup_type' => 'SSO with existing /forum link'
     620                ));
    482621            } else {   
    483622                if($pageData && is_object($pageData) && isset($pageData->guid)) {
     
    587726            set_transient('wtb_first_time_setup', 1, 30);
    588727        }
     728        // Clear category cache after successful login
     729        WebsiteToolboxForum\clearCategoryCache();
     730       
    589731        unset($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated']);
    590         wp_redirect("options-general.php?page=".WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolboxUpdateOptions");
     732        wp_safe_redirect("admin.php?page=".WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolboxUpdateOptions");
    591733        exit;
    592734    }
     
    613755}
    614756
    615 /* Purpose: to show settigns field into forum plugin settinga page.
     757/* Purpose: to show settings field into forum plugin settings page.
    616758Parameter: None
    617759Return: None */
     
    655797        WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_settings_update_section'
    656798    );
     799   
    657800    // To show "Update" button on forum settings page on WordPress admin panel.
    658801    add_settings_field(
     
    677820function forumDashboardUpdateUsername($args) {
    678821    $websitetoolbox_username = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_username');
    679     $html = $websitetoolbox_username.'&nbsp;&nbsp;<a href="options-general.php?page='.WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxoptions">Change</a>';
     822    $html = $websitetoolbox_username.'&nbsp;&nbsp;<a href="admin.php?page='.WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolboxUpdateOptions&change=1">Change</a>';
    680823    echo wp_kses($html, WebsiteToolboxInclude\wtbGetAllowedHTMLTags());
    681824}
     
    719862}
    720863
    721 /* Purpose: To show plugin settigns page as well as error and success message.
     864/* Purpose: To display the plugin's main (top-level) admin settings page, including error and success messages.
    722865Parameter: None
    723866Return: None */
     
    726869    WebsiteToolboxInclude\startForumSession();
    727870    $pid = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_pageid');
    728     if($message == '0') {
     871   
     872    // Check for settings updated parameter
     873    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display parameter from WordPress redirect
     874    $settingsUpdated = (isset($_GET['settings-updated']) && $_GET['settings-updated'] === 'true') || $message === 'success';
     875   
     876    if ($settingsUpdated) {
     877        echo '<div id="setting-error-settings_updated" class="updated notice is-dismissible"><p>Your settings have been updated.</p></div>';
     878    } elseif($message == '0') {
    729879        echo '<div id="setting-error-settings_updated" class="updated notice"><p>Your forum needs to <a href= "https://www.websitetoolbox.com/tool/members/domain?tool=mb" target="_blank">use a subdomain</a> for it to appear as embedded instead of full-screen in the Safari browser.</p></div>';
    730     } else if($message == 'success') {
    731         echo '<div id="setting-error-settings_updated" class="updated notice"><p>Your settings have been updated.</p></div>';
    732880    }
    733881    printTestRequestStatus();
     882   
     883    // Build form action URL properly using WordPress functions
     884    $formAction = admin_url('admin.php?page=' . WebsiteToolboxForum\globalVariables::$WTBPREFIX . 'websitetoolboxUpdateOptions');
    734885    ?>
    735886    <!-- create a form in the wordpress admin panel -->
    736887    <div class="wrap">
    737         <form name="form_lol" action="options-general.php?page=<?php echo esc_html((WebsiteToolboxForum\globalVariables::$WTBPREFIX)); ?>websitetoolboxUpdateOptions" method="POST" >
     888        <form name="form_lol" action="<?php echo esc_url($formAction); ?>" method="POST" >
    738889            <?php
    739890              wp_nonce_field('updateWtbSettings', 'update_wtplugin_nonce');
     
    749900Return: None */
    750901function onForumUpdateSettings() {
     902    // Check user capability first for security
     903    if (!current_user_can('manage_options')) {
     904        wp_die('You do not have sufficient permissions to access this page.');
     905    }
     906   
     907    // Check if user wants to change login credentials (GET request) or is submitting login form (POST with add_wtplugin_nonce)
     908    $isLoginForm = (isset($_GET['change']) && $_GET['change'] === '1') ||
     909                   (isset($_POST['add_wtplugin_nonce']) && !isset($_POST['update_wtplugin_nonce']));
     910   
     911    if ($isLoginForm) {
     912        // Show login form to change credentials or process login
     913        onForumDashboardLogin();
     914        return;
     915    }
     916   
    751917    if(!get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_user_roles')){
    752918        global $wp_roles;
     
    9501116                }
    9511117            }
    952             showForumUpdateSettingsPage('success');
     1118           
     1119            // Clear category cache when settings are updated
     1120            WebsiteToolboxForum\clearCategoryCache();
     1121           
     1122            // Redirect back to settings page with success message
     1123            wp_safe_redirect(add_query_arg('settings-updated', 'true', admin_url('admin.php?page=' . WebsiteToolboxForum\globalVariables::$WTBPREFIX . 'websitetoolboxUpdateOptions')));
    9531124            exit;
    9541125        }   
     
    10691240    $id = (int) $id;
    10701241    // allow for transaction statement
    1071     do_action('delete_user', $id);
    10721242    if ( 'novalue' === $reassign || null === $reassign ) {
    10731243        // phpcs:ignore WordPress.DB.DirectDatabaseQuery
     
    11021272        $wpdb->query("DELETE FROM $wpdb->usermeta WHERE user_id = %d, AND meta_key = '{%s}'", $id, $level_key);
    11031273    }
    1104     // allow for commit transaction
    1105     do_action('deleted_user', $id);
    11061274    return true;
    11071275}
     
    11191287            $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated'] = '';
    11201288            unset($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated']);
    1121         } else if($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated'] == 2) {
     1289        } elseif($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated'] == 2) {
    11221290            $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated'] = '';
    11231291            unset($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated']);
    11241292        }
    11251293
    1126     } else if($is_admin == 1 &&  isset($_SESSION['wtb_account_settings'])) {
     1294    } elseif($is_admin == 1 &&  isset($_SESSION['wtb_account_settings'])) {
    11271295        if($_SESSION['wtb_account_settings'] == 1) {
    11281296            echo wp_kses('<div id="wtb-warning" class="error"><p>Your Website Toolbox Forum account is currently disabled. Please <a href="https://www.websitetoolbox.com/tool/members/login/'.get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_username").'" target="_blank">login to your Website Toolbox account</a> for more information.</a></p></div>', WebsiteToolboxInclude\wtbGetAllowedHTMLTags());
    1129         } else if(isset($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtb_account_settings']) && $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtb_account_settings'] == 2) {
    1130             echo '<div id="wtb-warning" class="error"><p>The API Key integrated with the Website Toolbox Forum plugin is invalid. Please <a href="options-general.php?page=websitetoolboxoptions">log in</a> again to update it.</p></div>';
     1297        } elseif(isset($_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtb_account_settings']) && $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtb_account_settings'] == 2) {
     1298            $loginUrl = admin_url('admin.php?page=' . WebsiteToolboxForum\globalVariables::$WTBPREFIX . 'websitetoolboxUpdateOptions&change=1');
     1299            echo '<div id="wtb-warning" class="error"><p>The API Key integrated with the Website Toolbox Forum plugin is invalid. Please <a href="' . esc_url($loginUrl) . '">log in</a> again to update it.</p></div>';
    11311300        }
    11321301    }
     
    11691338        }
    11701339        $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbForumCheck'] = 1;
    1171     } else if ($is_admin == 1 && is_plugin_active('website-toolbox-forums/websitetoolbox.php') && (!get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_username") || !get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_api") || !get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_url"))) {
     1340    } elseif ($is_admin == 1 && is_plugin_active('website-toolbox-forums/websitetoolbox.php') && (!get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_username") || !get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_api") || !get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX."websitetoolbox_url"))) {
    11721341      $_SESSION[WebsiteToolboxForum\globalVariables::$WTBPREFIX.'wtbPluginActivated'] = 2;
    11731342    }
     
    12121381            // Add/update data if embeded option is enable.
    12131382            if($redirect_option==1) {
    1214                 $postTitle = $pageData->post_title;
     1383                $postTitle = $pageData && is_object($pageData) ? $pageData->post_title : '';
    12151384                if($wtbForumpageid) {
    12161385                    onForumPluginActivation('settings');
     
    12181387                // Create a new page for forum if forum is alrady integrated as a custom link on WordPress website.
    12191388                // User save settings after enable "Embed" option. So that didn't effect on custom link.
    1220                 if($pageData->post_type == 'nav_menu_item') {
     1389                if($pageData && is_object($pageData) && $pageData->post_type == 'nav_menu_item') {
    12211390                    $wtbForumpageid = createOrUpdateForumPage();
    12221391                    // Update page id in options table
     
    12431412                }
    12441413                // Add/update embed code for the forum page on WordPress.
    1245                 $embedPageUrl = $pageData->guid;
     1414                $embedPageUrl = $pageData && is_object($pageData) ? $pageData->guid : '';
    12461415                $wtb_embed_url = preg_replace('#^https?:#i', '', $wtb_url);
    12471416
     
    12511420                $postContent = '';
    12521421                $embedCode = getEmbedCodeHTML($wtb_embed_url, $wtb_url);
    1253                 if($pageData->post_content && strpos($pageData->post_content, $wtb_embed_url) !== false) {
     1422                if($pageData && is_object($pageData) && $pageData->post_content && strpos($pageData->post_content, $wtb_embed_url) !== false) {
    12541423                    $postContent = str_replace($embedCode, 'Embed Code', $pageData->post_content);
    12551424                }
     
    15191688        $response = testDeclineMemberRequest('getResponse');
    15201689        if($response) {
    1521             wp_redirect("options-general.php?page=websitetoolboxUpdateOptions&action=successRequest");
     1690            wp_safe_redirect("options-general.php?page=websitetoolboxUpdateOptions&action=successRequest");
    15221691            exit;
    15231692        } else {
    1524             wp_redirect("options-general.php?page=websitetoolboxUpdateOptions&action=failRequest");
     1693            wp_safe_redirect("options-general.php?page=websitetoolboxUpdateOptions&action=failRequest");
    15251694            exit;
    15261695        }
     
    15671736        update_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_pageid', $existingPageId);
    15681737        set_transient('wtb_used_existing_page', $existingPageId, 30);
     1738       
     1739        // Log when existing page with embed code is found and used
     1740        $pageData = get_post($existingPageId);
     1741        \WebsiteToolboxLogger\Logger::info('PAGE_CONNECT', 'Existing embed page found and connected', array(
     1742            'page_id' => $existingPageId,
     1743            'page_title' => $pageData ? $pageData->post_title : 'Unknown',
     1744            'page_url' => get_permalink($existingPageId),
     1745            'created_by' => 'manual'
     1746        ));
     1747       
    15691748        return $existingPageId;
    15701749    } else {
     
    16051784}
    16061785
     1786/* Purpose: Track when forum embed page is deleted manually
     1787Parameter: Post ID
     1788Return: None */
     1789function trackEmbedPageDeletion($postId) {
     1790    $forumPageId = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_pageid');
     1791   
     1792    // Check if the deleted page is the forum embed page
     1793    if ($postId == $forumPageId) {
     1794        $postData = get_post($postId);
     1795        if ($postData) {
     1796            \WebsiteToolboxLogger\Logger::warning('PAGE_DELETE', 'Forum embed page deleted manually', array(
     1797                'page_id' => $postId,
     1798                'page_title' => $postData->post_title,
     1799                'deleted_by' => 'manual',
     1800                'post_status' => $postData->post_status
     1801            ));
     1802        }
     1803    }
     1804}
     1805
     1806/* Purpose: Track when forum embed page is trashed manually
     1807Parameter: Post ID
     1808Return: None */
     1809function trackEmbedPageTrashed($postId) {
     1810    $forumPageId = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_pageid');
     1811   
     1812    // Check if the trashed page is the forum embed page
     1813    if ($postId == $forumPageId) {
     1814        $postData = get_post($postId);
     1815        if ($postData) {
     1816            \WebsiteToolboxLogger\Logger::warning('PAGE_TRASH', 'Forum embed page moved to trash', array(
     1817                'page_id' => $postId,
     1818                'page_title' => $postData->post_title,
     1819                'trashed_by' => 'manual',
     1820                'previous_status' => $postData->post_status
     1821            ));
     1822        }
     1823    }
     1824}
     1825
    16071826/* Purpose: Display admin message about page creation/connection
    16081827Parameter: None
    16091828Return: None */
    16101829function showPageActionMessage() {
     1830    // Always clear transients first to prevent them from persisting
    16111831    $wtbPageId = get_transient('wtb_used_existing_page');
    16121832    if ($wtbPageId) {
     
    16181838        }
    16191839    }
     1840   
     1841    // Don't show message if settings-updated parameter is present (handled by showForumUpdateSettingsPage)
     1842    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only display parameter
     1843    if (isset($_GET['settings-updated']) && $_GET['settings-updated'] === 'true') {
     1844        return;
     1845    }
     1846   
    16201847    if (empty($wtbPageId)) {
    16211848        return;
  • website-toolbox-forums/trunk/admin/css/websitetoolbox.css

    r3223937 r3439073  
    231231    margin-right: 15px;
    232232}
     233
     234/* Website Toolbox Menu Icon Styles */
     235#adminmenu .toplevel_page_websitetoolboxUpdateOptions .wp-menu-image img,
     236#adminmenu .toplevel_page_websitetoolboxoptions .wp-menu-image img {
     237    width: 20px;
     238    height: 20px;
     239    padding: 0;
     240    opacity: 0.6;
     241}
     242
     243#adminmenu .toplevel_page_websitetoolboxUpdateOptions:hover .wp-menu-image img,
     244#adminmenu .toplevel_page_websitetoolboxoptions:hover .wp-menu-image img,
     245#adminmenu .toplevel_page_websitetoolboxUpdateOptions.current .wp-menu-image img,
     246#adminmenu .toplevel_page_websitetoolboxoptions.current .wp-menu-image img {
     247    opacity: 1;
     248}
     249
     250/* Forum Logs Page Styles */
     251.wtb-logs-header {
     252    background: #fff;
     253    padding: 15px;
     254    border: 1px solid #ccd0d4;
     255    box-shadow: 0 1px 1px rgba(0,0,0,.04);
     256}
     257
     258.wtb-table-wrapper {
     259    overflow-x: auto;
     260    margin-bottom: 15px;
     261}
     262
     263.wtb-logs-table {
     264    min-width: 100%;
     265    table-layout: auto;
     266}
     267
     268.wtb-log-message {
     269    line-height: 1.6;
     270    word-wrap: break-word;
     271    overflow-wrap: break-word;
     272    max-width: 100%;
     273}
     274
     275.wtb-log-context {
     276    max-width: 100%;
     277    overflow-x: auto;
     278    overflow-y: hidden;
     279    margin-top: 10px;
     280    padding: 10px;
     281    background: #f5f5f5;
     282    border-left: 3px solid #0073aa;
     283    border-radius: 3px;
     284}
     285
     286.wtb-log-context pre {
     287    margin: 0;
     288    white-space: pre;
     289    word-wrap: normal;
     290    overflow-wrap: normal;
     291    max-width: none;
     292    min-width: 100%;
     293}
     294
     295.wtb-log-row td {
     296    vertical-align: top;
     297}
     298
     299/* Log Level Badge Styles */
     300.wtb-log-level {
     301    padding: 3px 8px;
     302    border-radius: 3px;
     303    font-size: 11px;
     304    font-weight: bold;
     305    display: inline-block;
     306}
     307
     308.wtb-log-level-error {
     309    background: #dc3232;
     310    color: white;
     311}
     312
     313.wtb-log-level-warning {
     314    background: #ffb900;
     315    color: #000;
     316}
     317
     318.wtb-log-level-info {
     319    background: #00a0d2;
     320    color: white;
     321}
     322
     323.wtb-log-level-debug {
     324    background: #808080;
     325    color: white;
     326}
     327
     328/* Make table responsive */
     329@media screen and (max-width: 1024px) {
     330    .wp-list-table.widefat th:nth-child(7),
     331    .wp-list-table.widefat td:nth-child(7) {
     332        display: none; /* Hide User Agent on smaller screens */
     333    }
     334   
     335    .wp-list-table.widefat th:nth-child(1),
     336    .wp-list-table.widefat td:nth-child(1) {
     337        width: 120px !important;
     338    }
     339}
     340
     341@media screen and (max-width: 782px) {
     342    .wp-list-table.widefat th:nth-child(5),
     343    .wp-list-table.widefat td:nth-child(5) {
     344        display: none; /* Hide User on mobile */
     345    }
     346   
     347    .wp-list-table.widefat th:nth-child(3),
     348    .wp-list-table.widefat td:nth-child(3) {
     349        width: 90px !important;
     350    }
     351   
     352    .wtb-log-message {
     353        font-size: 13px;
     354    }
     355   
     356    .wtb-toggle-context {
     357        margin-top: 5px !important;
     358        margin-left: 0 !important;
     359    }
     360}
     361
     362.tablenav .actions select {
     363    float: none;
     364}
  • website-toolbox-forums/trunk/admin/js/websitetoolbox.js

    r3123304 r3439073  
    33    wtbxPlugin.admin.disableSSOForAllUsersRole();
    44    wtbxPlugin.admin.showOptionsForUserRoles();
     5    wtbxPlugin.admin.toggleLogContext();
    56    var userRolesCheckbox = document.querySelector('input[name="user_roles[]"]');
    67    if (userRolesCheckbox) {
     
    9394            });
    9495        }
     96    },
     97
     98    toggleLogContext: function() {
     99        jQuery(document).off('click', '.wtb-toggle-context').on('click', '.wtb-toggle-context', function(e) {
     100            e.preventDefault();
     101            e.stopPropagation();
     102           
     103            var $button = jQuery(this);
     104            var logId = $button.data('log-id');
     105            var $context = jQuery('#context-' + logId);
     106           
     107            if ($context.is(':visible')) {
     108                $context.slideUp(200);
     109                $button.text('Show Context');
     110            } else {
     111                jQuery('.wtb-log-context').slideUp(200);
     112                jQuery('.wtb-toggle-context').text('Show Context');
     113               
     114                $context.slideDown(200);
     115                $button.text('Hide Context');
     116            }
     117            return false;
     118        });
    95119    }
    96120};
  • website-toolbox-forums/trunk/admin/websitetoolbox_sidebar.php

    r3395282 r3439073  
    1 <?php 
     1<?php
    22namespace WebsiteToolboxAdminSidebar;
     3
     4// If this file is called directly, abort.
     5if ( ! defined( 'ABSPATH' ) ) {
     6    exit;
     7}
     8
    39use WebsiteToolboxForum;
    410use WebsiteToolboxForum\globalVariables;
     
    612function getAllCategoriesForSideBar(){
    713    global $pagenow;
     14   
     15    // Cache categories to avoid duplicate API calls during the same request
     16    static $cachedForumCategories = null;
     17   
     18    // Return cached data if available
     19    if ($cachedForumCategories !== null) {
     20        return $cachedForumCategories;
     21    }
     22   
    823    $forumCategories = array();
    9     if($pagenow == 'post-new.php' || $pagenow == 'post.php' ){
     24   
     25    // Only fetch categories if on post/page edit screens and API key is configured
     26    if(($pagenow == 'post-new.php' || $pagenow == 'post.php') && get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_api')){
    1027        $categoryList = WebsiteToolboxForum\getCategoryList();
    1128        if(is_array($categoryList)){
     
    1330        }
    1431    }
     32   
     33    // Cache the result before returning
     34    $cachedForumCategories = $forumCategories;
    1535    return $forumCategories;
    1636}
     
    2444/* show metabox in classic editor */
    2545function custom_meta_box_markup($post){
     46    // Don't render metabox during deletion, trash, or untrash operations
     47    // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a read-only check, not processing form data
     48    if(isset($_GET['action']) && in_array($_GET['action'], array('delete', 'trash', 'untrash'))){
     49        return;
     50    }
     51   
    2652    wp_nonce_field(basename(__FILE__), "meta-box-nonce");
    2753    $forumCategories = getAllCategoriesForSideBar();
     
    5177        $existingTopicId    = get_post_meta($post->ID,'forum_topicId',true);
    5278        if($existingTopicId && $existingTopicId != ''){
    53             $getTopicId         = WebsiteToolboxForum\apiRequest('GET', "/topics/".$existingTopicId);
     79            // Check if API key is configured and not in a deletion/trash context
     80            $apiKey = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_api');
     81            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a read-only check, not processing form data
     82            $isDeletionAction = (isset($_GET['action']) && in_array($_GET['action'], array('delete', 'trash', 'untrash')));
     83           
     84            if(!empty($apiKey) && !$isDeletionAction){
     85                $getTopicId         = WebsiteToolboxForum\apiRequest('GET', "/topics/".$existingTopicId);
     86            }
    5487        }
    5588        if(isset($getTopicId->status)){
     
    127160/* show sidebar in block editor */
    128161function sidebar_plugin_register() {
     162    global $pagenow;
     163   
     164    // Always register post meta for block editor (required for saving data)
     165    register_post_meta( '', 'website_toolbox_forum_postUrl', array(
     166            'single'        => true,
     167            'show_in_rest'  => true,
     168            'type'          => 'string',
     169        )
     170    );
     171    register_post_meta( '', 'website_toolbox_forum_category', array(
     172            'single'        => true,
     173            'show_in_rest'  => true,
     174            'type'          => 'string',
     175            'auth_callback' => function() {
     176                return current_user_can( 'edit_posts' );
     177            }
     178        )
     179    );
     180    register_post_meta( '', 'website_toolbox_publish_on_forum', array(
     181            'single'        => true,
     182            'show_in_rest'  => true,
     183            'type'          => 'string',
     184            'auth_callback' => function() {
     185                return current_user_can( 'edit_posts' );
     186            }
     187        )
     188    );
     189   
     190    // Only proceed with sidebar UI registration on post/page edit screens
     191    if (!is_admin() || !in_array($pagenow, array('post-new.php', 'post.php'))) {
     192        return;
     193    }
     194   
     195    // Don't run during deletion, trash, or untrash operations
     196    if(isset($_GET['action']) && in_array($_GET['action'], array('delete', 'trash', 'untrash'))){
     197        return;
     198    }
     199   
     200    // Only proceed if API key is configured
     201    if(!get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_api')){
     202        return;
     203    }
     204   
    129205    $forumCategories = getAllCategoriesForSideBar();
    130     if(get_option("websitetoolbox_api")){
    131         $postTypeDefault    = array();
    132         $sidebarCategories   = array();
     206    $postTypeDefault    = array();
     207    $sidebarCategories   = array();
    133208        wp_register_script(
    134209            'plugin-sidebar-js',
     
    176251
    177252        if(isset($_GET['post'])){
    178             $existingForumCategory      = isset($_GET['post']) ? sanitize_text_field(wp_unslash($_GET['post']),'website_toolbox_forum_category',true) : '';
    179             $existingTopicId            = isset($_GET['post']) ? sanitize_text_field(wp_unslash($_GET['post']),'forum_topicId',true) : '';
     253            $postId = sanitize_text_field(wp_unslash($_GET['post']));
     254            $existingForumCategory      = get_post_meta($postId, 'website_toolbox_forum_category', true);
     255            $existingTopicId            = get_post_meta($postId, 'forum_topicId', true);
    180256            if($existingTopicId){
    181                 $latestForumCategory    = WebsiteToolboxForum\checkTopicCategory($existingTopicId,$existingForumCategory);
    182             }
    183 
    184         }
    185         register_post_meta( '', 'website_toolbox_forum_postUrl', array(
    186                 'single'        => true,
    187                 'show_in_rest'  => true,
    188                 'type'          => 'string',
    189             )
    190         );
    191         register_post_meta( '', 'website_toolbox_forum_category', array(
    192                 'single'        => true,
    193                 'show_in_rest'  => true,
    194                 'type'          => 'string',
    195                 'auth_callback' => function() {
    196                     return current_user_can( 'edit_posts' );
    197                 }
    198             )
    199         );
    200         register_post_meta( '', 'website_toolbox_publish_on_forum', array(
    201                 'single'        => true,
    202                 'show_in_rest'  => true,
    203                 'type'          => 'string',
    204                 'auth_callback' => function() {
    205                     return current_user_can( 'edit_posts' );
    206                 }
    207             )
    208         );
     257                // Check if API key is configured and not in a deletion/trash context
     258                $apiKey = get_option(WebsiteToolboxForum\globalVariables::$WTBPREFIX.'websitetoolbox_api');
     259                $isDeletionAction = (isset($_GET['action']) && in_array($_GET['action'], array('delete', 'trash', 'untrash')));
     260               
     261                if(!empty($apiKey) && !$isDeletionAction){
     262                    $latestForumCategory    = WebsiteToolboxForum\checkTopicCategory($existingTopicId,$existingForumCategory);
     263                }
     264            }
     265
     266        }
    209267        $postTypeDefault= array(
    210268            'pageContent'   => get_option("websitetoolbox_page_content"),
     
    217275        wp_localize_script( 'plugin-sidebar-js', 'sidebarCategories', $sidebarCategories );
    218276        wp_localize_script( 'plugin-sidebar-js', 'defaultParameter', $postTypeDefault );
    219     }
    220277}
    221278
  • website-toolbox-forums/trunk/core/events.php

    r3395282 r3439073  
    44 */
    55namespace WebsiteToolboxEvents;
     6
     7// If this file is called directly, abort.
     8if ( ! defined( 'ABSPATH' ) ) {
     9    exit;
     10}
     11
    612use WebsiteToolboxForum;
    713use WebsiteToolboxAdmin;
     
    213219            }
    214220            WebsiteToolboxForum\resetCookieOnLogout();
    215             wp_redirect($wtbLogoutUrl);
     221            wp_safe_redirect($wtbLogoutUrl);
    216222            exit;
    217223        }
  • website-toolbox-forums/trunk/forumHook.php

    r3380096 r3439073  
    11<?php
     2
     3// If this file is called directly, abort.
     4if ( ! defined( 'ABSPATH' ) ) {
     5    exit;
     6}
     7
    28add_action('admin_menu', 'WebsiteToolboxAdmin\\showForumDashboardMenu');
     9add_action('admin_menu', 'WebsiteToolboxLogs\\addLogsMenu');
     10add_action('admin_init', 'WebsiteToolboxLogs\\handleDownloadAction');
    311add_action('admin_init', 'WebsiteToolboxAdmin\\showForumDashboardLoginPage');
    412add_action('admin_init', 'WebsiteToolboxAdmin\\showForumDashboardUpdatePage');
    513add_action('admin_init', 'WebsiteToolboxAdmin\\redirectAfterActivation' );
    6 add_action( 'admin_init', 'WebsiteToolboxInclude\\startForumSession');
    714add_action('wp_head', 'WebsiteToolboxForum\\publishForumPage');
     15/* SSO login/logout functionality on front-end pages */
     16add_action('wp_head', 'WebsiteToolboxForum\\ssoLoginLogout', 999);
    817/* admin_notices to print notice(message) on admin section */
    918add_action('admin_notices', 'WebsiteToolboxAdmin\\showWarnings');
     
    1726/* user_register hook called when a new account creates from from wordpress site (front end/back end) */
    1827add_action('user_register', 'WebsiteToolboxEvents\\createUserOnForum');
    19 /* print IMG tags to the footer if needed */
    20 add_action('wp_head','WebsiteToolboxForum\\ssoLoginLogout', 999);
    2128/* print IMG tags to the admin login page if user redirected to login page after logged-out. */
    2229add_action('login_head', 'WebsiteToolboxForum\\ssoLoginLogout');
     
    2835add_action( 'login_init', 'WebsiteToolboxAdmin\\checkReferelUrlOnLogin' );
    2936/* hooks for create topic on forum automatically */
    30 add_action( 'after_setup_theme', 'wtCheckForSidebar' );
    31 function wtCheckForSidebar() {
     37add_action( 'after_setup_theme', 'websitetoolbox_check_for_sidebar' );
     38function websitetoolbox_check_for_sidebar() {
    3239    global $pagenow;
    3340
     
    3845    }
    3946}
    40 add_action( 'init', 'WebsiteToolboxInclude\\startForumSession');
    4147add_action( 'init', 'WebsiteToolboxInclude\\startForumSession');
    4248add_action( 'init', 'WebsiteToolboxAdminSidebar\\sidebar_plugin_register',10 );
     
    7682add_action('show_user_profile', 'WebsiteToolboxForum\\ssoLogoutOnUpdatePassword');
    7783add_action('admin_notices', 'WebsiteToolboxAdmin\\showPageActionMessage');
     84/* Track when forum embed page is trashed manually */
     85add_action('wp_trash_post', 'WebsiteToolboxAdmin\\trackEmbedPageTrashed');
     86/* Track when forum embed page is deleted manually */
     87add_action('before_delete_post', 'WebsiteToolboxAdmin\\trackEmbedPageDeletion');
    7888
    7989?>
  • website-toolbox-forums/trunk/readme.txt

    r3408412 r3439073  
    44Requires at least: 3.0.0
    55Tested up to: 6.9
    6 Stable Tag: 2.1.1
     6Stable Tag: 2.1.2
    77License: GPL-2.0-or-later
    88License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    107107== Changelog ==
    108108
     109= 2.1.2 =
     110* Added an error log feature in the plugin so that the user can download the log if it exists. I also created a separate menu for Website Toolbox in the WordPress admin panel instead of displaying it under Settings.
     111
    109112= 2.1.1 =
    110113* Renamed plugin from community to forum.
  • website-toolbox-forums/trunk/uninstall.php

    r3026489 r3439073  
    11<?php
    22
     3// If uninstall not called from WordPress, exit
     4if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
     5    exit;
     6}
     7
    38include_once plugin_dir_path( __FILE__ ) . 'websitetoolbox.php';
     9
     10/**
     11 * Handle plugin uninstallation
     12 * Notifies the forum about the plugin being uninstalled
     13 */
     14function websitetoolbox_process_uninstall() {
    415    $fields = array(
    516            'action' => 'uninstallPlugin',
     
    1021
    1122    $response_array = wp_remote_post(WebsiteToolboxForum\globalVariables::$WTBSETTINGSPAGEURL, array('method' => 'POST', 'body' => $fields));
     23   
     24    return $response_array;
     25}
     26
     27// Execute uninstall process
     28websitetoolbox_process_uninstall();
    1229
    1330?>
  • website-toolbox-forums/trunk/websitetoolbox.php

    r3395282 r3439073  
    11<?php
    2 
    32/**
    43 * @package Website Toolbox Forum
     
    1211 * Author:      Website Toolbox
    1312 * Author URI:  https://www.websitetoolbox.com/wordpress
    14  * Version:     2.1.1
     13 * Version:     2.1.2
    1514 * License: GPL-2.0-or-later
    1615 * License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    1918namespace WebsiteToolboxForum;
    2019
     20// If this file is called directly, abort.
     21if ( ! defined( 'ABSPATH' ) ) {
     22    exit;
     23}
     24
     25// Logging configuration constants
     26if (!defined('WTB_LOGGING_ENABLED')) {
     27    define('WTB_LOGGING_ENABLED', 1);
     28}
     29if (!defined('WTB_LOG_RETENTION_DAYS')) {
     30    define('WTB_LOG_RETENTION_DAYS', 30);
     31}
    2132
    2233/* Class to define plugin specific global variable  */
     
    3849include("forumHook.php");
    3950include("core/events.php");
     51include("core/logger.php");
     52
     53// Initialize logger immediately after loading logger class
     54// This ensures the database table exists before any logging calls
     55try {
     56    \WebsiteToolboxLogger\Logger::init();
     57} catch (\Exception $e) {
     58    // Use error_log as fallback since logger initialization failed
     59    // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log
     60    error_log(sprintf(
     61        'Website Toolbox Forum: Logger initialization failed - %s in %s on line %d',
     62        $e->getMessage(),
     63        basename($e->getFile()),
     64        $e->getLine()
     65    ));
     66}
     67
    4068include("admin/admin.php");
     69include("admin/logs.php");
     70
     71// Enqueue client OS detection script for accurate logging
     72function enqueueClientInfoScript() {
     73    wp_enqueue_script(
     74        'wtb-client-info',
     75        plugins_url('core/client-info.js', __FILE__),
     76        array(),
     77        globalVariables::$VERSION,
     78        true
     79    );
     80}
     81add_action('wp_enqueue_scripts', 'WebsiteToolboxForum\\enqueueClientInfoScript');
     82add_action('admin_enqueue_scripts', 'WebsiteToolboxForum\\enqueueClientInfoScript');
    4183
    4284function redirectionToAvoidAMP() {
     
    58100            $updatedPageUrl =  remove_query_arg('amp', $currentPageUrl);
    59101            $updatedPageUrl =  add_query_arg( 'noamp', 'mobile', $updatedPageUrl);
    60             wp_redirect( $updatedPageUrl );
     102            wp_safe_redirect( $updatedPageUrl );
    61103            exit;
    62104        }
     
    69111function embedContent($content) {
    70112    $websitetoolboxpage_id = get_option(globalVariables::$WTBPREFIX.'websitetoolbox_pageid');
    71     $page_content = get_page($websitetoolboxpage_id);
     113    $page_content = get_post($websitetoolboxpage_id);
     114   
     115    // Log error if page content cannot be loaded
     116    if (empty($page_content)) {
     117        \WebsiteToolboxLogger\Logger::error('EMBED', 'Failed to load forum page content', \WebsiteToolboxLogger\Logger::sanitizeLogData(array(
     118            'page_id' => $websitetoolboxpage_id,
     119            'url' => isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : ''
     120        )));
     121        return $content;
     122    }
     123   
    72124    $page_content = $page_content->post_content;
    73125    $theme_data = wp_get_theme();
     
    203255        $isSSOEnableForRole = isSsoEnableForUserRole($userObj);
    204256        if (isset($isSSOEnableForRole) && $isSSOEnableForRole == 1) {
     257            \WebsiteToolboxLogger\Logger::info('SSO', 'Attempting SSO login for user', array('user_id' => $wpLoggedinUserid, 'username' => $userObj->user_login));
     258           
    205259            if (!empty($forumAddressChanged)) {
    206260                resetCookieOnLogout();
     261                \WebsiteToolboxLogger\Logger::info('SSO', 'Forum address changed, resetting cookies', array('user_id' => $wpLoggedinUserid));
    207262            }
    208263            $responseArray = ssoHttpRequest($userObj);
     
    211266            if(isset($responseArray['authtoken'])) {
    212267                saveAuthToken($responseArray['authtoken'],$responseArray['wtbUserid']);
     268                \WebsiteToolboxLogger\Logger::info('SSO', 'SSO login successful', array('user_id' => $wpLoggedinUserid, 'wtb_user_id' => $responseArray['wtbUserid']));
    213269                return $responseArray['authtoken'];
     270            } else {
     271                \WebsiteToolboxLogger\Logger::error('SSO', 'SSO login failed - no authtoken returned', array('user_id' => $wpLoggedinUserid));
    214272            }
    215273        }
     
    263321            $fields['pw'] = sanitize_text_field(wp_unslash($_POST['pwd']));
    264322        }
     323       
     324        \WebsiteToolboxLogger\Logger::debug('SSO_API', 'Sending SSO authentication request', array('url' => $URL, 'user' => $userObj->user_login, 'user_id' => $userObj->ID));
     325       
    265326        // Send http or https request to get authentication token.
    266327        $response_array = wp_remote_post($URL, array('method' => 'POST', 'body' => $fields));
     
    269330            $response = trim(wp_remote_retrieve_body($response_array));
    270331            $response = json_decode($response);
    271             if($response && $response->{'authtoken'} != "") {
     332            if($response && !empty($response->{'authtoken'})) {
    272333                $responseArray = array(
    273334                    'authtoken' => $response->{'authtoken'},
    274                     'wtbUserid' => $response->{'userid'}
     335                    'wtbUserid' => isset($response->{'userid'}) ? $response->{'userid'} : null
    275336                );
     337                \WebsiteToolboxLogger\Logger::info('SSO_API', 'SSO authentication successful', array('user' => $userObj->user_login, 'wtb_user_id' => isset($response->{'userid'}) ? $response->{'userid'} : null));
    276338                return $responseArray;
    277             }
    278         } else if (isset($_GET['from']) && $_GET['from'] == globalVariables::$WTBPREFIX.globalVariables::$loginParam) {
    279             wp_die("Error message during discussion forum SSO: " . esc_html($response_array->get_error_message()));
    280         }
     339            } else {
     340                // Log minimal metadata - avoid logging full response to prevent credential leakage
     341                $responseType = is_object($response) ? 'object' : (is_array($response) ? 'array' : gettype($response));
     342                $hasAuthtoken = is_object($response) && isset($response->{'authtoken'});
     343                \WebsiteToolboxLogger\Logger::error('SSO_API', 'SSO authentication failed - invalid response', array(
     344                    'user' => $userObj->user_login,
     345                    'response_type' => $responseType,
     346                    'has_authtoken' => $hasAuthtoken,
     347                    'authtoken_empty' => $hasAuthtoken && empty($response->{'authtoken'})
     348                ));
     349                if (isset($_GET['from']) && $_GET['from'] == globalVariables::$WTBPREFIX.globalVariables::$loginParam) {
     350                    wp_die("Error message during discussion forum SSO: Invalid authentication response");
     351                }
     352            }
     353        } else {
     354            \WebsiteToolboxLogger\Logger::error('SSO_API', 'SSO authentication request failed', \WebsiteToolboxLogger\Logger::sanitizeLogData(array('user' => $userObj->user_login, 'error' => $response_array->get_error_message())));
     355            if (isset($_GET['from']) && $_GET['from'] == globalVariables::$WTBPREFIX.globalVariables::$loginParam) {
     356                wp_die("Error message during discussion forum SSO: " . esc_html($response_array->get_error_message()));
     357            }
     358        }
     359    } else {
     360        \WebsiteToolboxLogger\Logger::warning('SSO_API', 'SSO request attempted but API key not configured', array('user_id' => $userObj->ID));
    281361    }
    282362}
     
    357437    $theArray = array();
    358438    $prefix = esc_html(globalVariables::$WTBPREFIX);
    359     $theqsl = "SELECT * FROM $wpdb->postmeta a, $wpdb->posts b  WHERE a.`post_id`=b.`ID` AND b.`post_status`!='trash' AND (a.`meta_key` = '".globalVariables::$WTBPREFIX."_wtbredirect_active' || a.`meta_key` = '".globalVariables::$WTBPREFIX."_links_to' || a.`meta_key` = '".globalVariables::$WTBPREFIX."_links_to_target' || a.`meta_key` = '".globalVariables::$WTBPREFIX."_links_to_type') ORDER BY a.`post_id` ASC;"; 
     439   
     440    // Prepare meta keys with proper escaping
     441    $meta_key_redirect = $prefix . '_wtbredirect_active';
     442    $meta_key_links_to = $prefix . '_links_to';
     443    $meta_key_links_to_target = $prefix . '_links_to_target';
     444    $meta_key_links_to_type = $prefix . '_links_to_type';
     445   
     446    $theqsl = $wpdb->prepare(
     447        "SELECT * FROM {$wpdb->postmeta} a, {$wpdb->posts} b
     448        WHERE a.post_id = b.ID
     449        AND b.post_status != %s
     450        AND (a.meta_key = %s OR a.meta_key = %s OR a.meta_key = %s OR a.meta_key = %s)
     451        ORDER BY a.post_id ASC",
     452        'trash',
     453        $meta_key_redirect,
     454        $meta_key_links_to,
     455        $meta_key_links_to_target,
     456        $meta_key_links_to_type
     457    );
     458   
    360459    // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.DirectDatabaseQuery
    361460    $thetemp = $wpdb->get_results($theqsl);
     
    406505    $wpLoggedinUserid   = get_current_user_id();
    407506    $userObj            = new \WP_User($wpLoggedinUserid);
     507   
    408508    if(!empty($currentPageId) && $currentPageId == get_option(globalVariables::$WTBPREFIX.'websitetoolbox_pageid') && is_user_logged_in()) {
    409509        if(!empty($userObj) && !isSsoEnableForUserRole($userObj)) {
     
    417517            if(isset($_GET['ssoDisabled'])) {
    418518                $content = wp_kses('<div id="notice-warning" class="notice notice-warning is-dismissible"><p>Sorry! You don’t have permission to log in to the forum.<p></div>', \WebsiteToolboxInclude\wtbGetAllowedHTMLTags());
    419             } else if(get_option(globalVariables::$WTBPREFIX."websitetoolbox_redirect") == '') {
    420                 wp_redirect(get_option(globalVariables::$WTBPREFIX."websitetoolbox_url"));
     519            } elseif(get_option(globalVariables::$WTBPREFIX."websitetoolbox_redirect") == '') {
     520                wp_safe_redirect(get_option(globalVariables::$WTBPREFIX."websitetoolbox_url"));
    421521                exit();
    422522            }
    423         } else if(get_option(globalVariables::$WTBPREFIX."websitetoolbox_redirect") != '') {
     523        } elseif(get_option(globalVariables::$WTBPREFIX."websitetoolbox_redirect") != '') {
    424524            $embedSrcCount = preg_match('/id="embedded_forum"\ssrc=(["\'])(.*?)\1/', $content, $match);
    425525            if($embedSrcCount == 1) {
     
    520620Return: JSON data */
    521621function apiRequest($method, $path, $data = '', $header = '') {
     622    // Check if API key is configured before making the request
     623    $api_key = get_option(globalVariables::$WTBPREFIX . 'websitetoolbox_api');
     624    if (empty($api_key)) {
     625        \WebsiteToolboxLogger\Logger::debug('API', 'API key not configured, skipping request', \WebsiteToolboxLogger\Logger::sanitizeLogData(array('method' => $method, 'path' => $path)));
     626        return (object) array(
     627            'status' => 'error',
     628            'error' => (object) array(
     629                'code' => 'InvalidAPIkeyOrMissing',
     630                'message' => 'API key is not configured'
     631            )
     632        );
     633    }
     634   
    522635    $url = globalVariables::$WTBAPIPAGEURL . $path;
    523636    if (!empty($data) && strtoupper($method) == "GET") {
    524         $url = sprintf("%s?%s", $url, http_build_query($data));
     637        $queryString = http_build_query($data);
     638        $url = sprintf("%s?%s", $url, $queryString);
    525639    }
    526640    $args = array(
    527641        'headers' => array(
    528             'x-api-key' => get_option(globalVariables::$WTBPREFIX . 'websitetoolbox_api'),
     642            'x-api-key' => $api_key,
    529643            'Content-Type' => 'application/json',
    530644            'User-Agent' => 'Website Toolbox WordPress Plugin',
     
    532646        'body' => (!empty($data) && strtoupper($method) == "POST") ? wp_json_encode($data) : null,
    533647    );
     648   
     649    \WebsiteToolboxLogger\Logger::debug('API', 'API request initiated', \WebsiteToolboxLogger\Logger::sanitizeLogData(array('method' => $method, 'path' => $url)));
     650   
    534651    if (strtoupper($method) == "POST") {
    535652        $response = wp_remote_post($url, $args);
    536     } else if (strtoupper($method) == "GET") {
     653    } elseif (strtoupper($method) == "GET") {
    537654        $response = wp_remote_get($url, $args);
     655    } else {
     656        // Unsupported HTTP method
     657        \WebsiteToolboxLogger\Logger::error('API', 'Unsupported HTTP method', \WebsiteToolboxLogger\Logger::sanitizeLogData(array('method' => $method, 'path' => $url)));
     658        return false;
    538659    }
     660   
     661    if (is_wp_error($response)) {
     662        \WebsiteToolboxLogger\Logger::error('API', 'API request failed', \WebsiteToolboxLogger\Logger::sanitizeLogData(array('method' => $method, 'path' => $url, 'error' => $response->get_error_message())));
     663    } else {
     664        $response_code = wp_remote_retrieve_response_code($response);
     665        $response_body = wp_remote_retrieve_body($response);
     666        if ($response_code >= 400) {
     667            // Log minimal metadata - avoid logging full response body to prevent credential leakage
     668            $response_length = strlen($response_body);
     669            $response_preview = $response_length > 0 ? substr($response_body, 0, 100) : '';
     670           
     671            // Parse response to check for specific error codes
     672            $response_data = json_decode($response_body);
     673            $error_code = isset($response_data->error->code) ? $response_data->error->code : '';
     674           
     675            // 404 errors are expected (e.g., topic not found when checking if it exists)
     676            // 400 errors with InvalidAPIkeyOrMissing are expected when plugin is not configured
     677            // Log them as DEBUG instead of WARNING
     678            $expected_errors = array('InvalidAPIkeyOrMissing', 'MissingAPIkey', 'InvalidAPIkey');
     679            if ($response_code == 404 || ($response_code == 400 && in_array($error_code, $expected_errors))) {
     680                $log_level = 'debug';
     681            } else {
     682                $log_level = 'warning';
     683            }
     684           
     685            \WebsiteToolboxLogger\Logger::$log_level('API', 'API request returned error status', \WebsiteToolboxLogger\Logger::sanitizeLogData(array(
     686                'method' => $method,
     687                'path' => $url,
     688                'status_code' => $response_code,
     689                'response_length' => $response_length,
     690                'response_preview' => $response_preview // First 100 chars, will be sanitized
     691            )));
     692        } else {
     693            \WebsiteToolboxLogger\Logger::debug('API', 'API request successful', array('method' => $method, 'path' => $url, 'status_code' => $response_code));
     694        }
     695    }
     696   
    539697    return json_decode(wp_remote_retrieve_body($response));
    540698}
     
    576734    if($userRoles && $userRoles['users'] == "all_users") {
    577735        $isSSOEnable = 1;
    578     } else if($userRoles && $userRoles['users'] == "no_users") {
     736    } elseif($userRoles && $userRoles['users'] == "no_users") {
    579737        $isSSOEnable = 0;
    580738    } else {
     
    586744                $isSSOEnable = 1;
    587745            }
    588         } else if($roleArrayLength && $roleArrayLength > 1) {
     746        } elseif($roleArrayLength && $roleArrayLength > 1) {
    589747            for($i = 0; $i < $roleArrayLength; $i++) {
    590748                if(in_array($currentUserRole[$i], $userRoles)) {
     
    613771//code for create post automatically on forum start
    614772function getCategoryList(){
    615     $limit  = array("limit"=>'100');
    616     $result     = apiRequest('GET', "/categories", $limit);
     773    // Use WordPress transient cache to avoid duplicate API calls across requests
     774    $cacheKey = globalVariables::$WTBPREFIX . 'category_list_cache';
     775    $lockKey = globalVariables::$WTBPREFIX . 'category_list_lock';
     776   
     777    // Check if cached data exists
     778    $cachedCategories = get_transient($cacheKey);
     779    if ($cachedCategories !== false) {
     780        return $cachedCategories;
     781    }
     782   
     783    // Check if another process is already fetching categories
     784    $isLocked = get_transient($lockKey);
     785    if ($isLocked !== false) {
     786        // Wait briefly and check cache again - another request might be populating it
     787        usleep(100000); // Wait 100ms
     788        $cachedCategories = get_transient($cacheKey);
     789        if ($cachedCategories !== false) {
     790            return $cachedCategories;
     791        }
     792        // If still not available, proceed anyway (lock might be stale)
     793    }
     794   
     795    // Set lock to prevent concurrent requests
     796    set_transient($lockKey, true, 10); // Lock for 10 seconds max
     797   
     798    $limit = array("limit"=>'100');
     799    $result = apiRequest('GET', "/categories", $limit);
     800   
     801    // Release lock
     802    delete_transient($lockKey);
     803   
    617804    if(isset($result->{'data'})){
    618         return $result->{'data'};
     805        $categories = $result->{'data'};
     806        // Cache for 5 minutes (300 seconds) to reduce API calls
     807        set_transient($cacheKey, $categories, 300);
     808        return $categories;
    619809    }else{
    620     return "There was an error returning the category list from Website Toolbox.";
     810        $errorMessage = "There was an error returning the category list from Website Toolbox.";
     811        // Cache error response for 1 minute to prevent repeated failed requests
     812        set_transient($cacheKey, $errorMessage, 60);
     813        return $errorMessage;
    621814    }
    622815}
     816
     817/* Purpose: Clear cached category list
     818 * This should be called when forum settings are updated
     819 */
     820function clearCategoryCache(){
     821    $cacheKey = globalVariables::$WTBPREFIX . 'category_list_cache';
     822    $lockKey = globalVariables::$WTBPREFIX . 'category_list_lock';
     823    delete_transient($cacheKey);
     824    delete_transient($lockKey);
     825}
     826
    623827function checkTopicCategory($topicId,$categoryId){
    624828    global $post;
     
    657861function addUpdateTopic($defaultCategoryId,$defaultContentType,$post){
    658862    $post_id    = $post->ID;
     863   
    659864    delete_post_meta( $post_id, 'website_toolbox_forum_publishing_error' );
    660865    $current_user       = wp_get_current_user();
    661866    $userName           = $current_user->user_login;
    662867    $title              = wp_strip_all_tags( $post->post_title );
    663     $title              = apply_filters( 'wpdc_publish_format_title', $title, $post_id );
     868    $title              = apply_filters( 'websitetoolbox_publish_format_title', $title, $post_id );
    664869    $current_post_type  = get_post_type( $post_id );
    665870    $publishFullPost    = $defaultContentType;
     
    669874    }
    670875     if($publishFullPost != 0){
     876        // Check if API key is configured before making any API requests
     877        $apiKey = get_option(globalVariables::$WTBPREFIX.'websitetoolbox_api');
     878        if(empty($apiKey)){
     879            return false;
     880        }
     881       
    671882        if($publishFullPost === 1){
    672883            if($post->post_excerpt){
     
    7891000function checkPostStatus($post_id,$post){
    7901001    $publish_status     = $post->post_status;
    791     $publish_private    = apply_filters( 'wpdc_publish_private_post', false, $post_id );
     1002    $publish_private    = apply_filters( 'websitetoolbox_publish_private_post', false, $post_id );
    7921003    if ( wp_is_post_revision( $post_id )
    7931004         || ( $publish_status != 'publish'  )
     
    9601171            $embedPageUrl = get_option(globalVariables::$WTBPREFIX."websitetoolbox_embedUrl");
    9611172            $embedPageUrl = add_query_arg( 'ssoDisabled', 1, $embedPageUrl);
    962             wp_redirect($embedPageUrl);
     1173            wp_safe_redirect($embedPageUrl);
    9631174            exit();
    9641175        }
    965     } else if(!$logoutRequst && !empty($wpLoggedinUserid) && !empty($userObj) && isSsoEnableForUserRole($userObj)) {
     1176    } elseif(!$logoutRequst && !empty($wpLoggedinUserid) && !empty($userObj) && isSsoEnableForUserRole($userObj)) {
    9661177        $redirectUrl = \WebsiteToolboxInclude\getRedirectURL();
    9671178        if(!empty($redirectUrl)) {
     
    9701181                $embedPageUrl = add_query_arg( 'authtoken', sanitize_text_field(wp_unslash($_COOKIE[globalVariables::$WTBPREFIX.'wt_logout_token'])), $embedPageUrl);
    9711182            }   
    972             wp_redirect($embedPageUrl);
     1183            wp_safe_redirect($embedPageUrl);
    9731184            exit();
    9741185        }
  • website-toolbox-forums/trunk/wt_forum_include.php

    r3342958 r3439073  
    11<?php
     2namespace WebsiteToolboxInclude;
    23
    3 namespace WebsiteToolboxInclude;
     4// If this file is called directly, abort.
     5if ( ! defined( 'ABSPATH' ) ) {
     6  exit;
     7}
     8
    49use WebsiteToolboxForum;
    510
Note: See TracChangeset for help on using the changeset viewer.