Plugin Directory

Changeset 3448236


Ignore:
Timestamp:
01/27/2026 10:13:22 PM (2 months ago)
Author:
ugoltsev
Message:

Release 0.8.1

Location:
ask-my-content/trunk
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • ask-my-content/trunk/ask-my-content.php

    r3445867 r3448236  
    44 * Plugin Name:       Ask My Content - AI Q&A Chatbot
    55 * Description:       AI-powered Q&A chatbot, allowing users to ask questions and receive answers sourced from the site’s own posts and pages.
    6  * Version:           0.8.0
     6 * Version:           0.8.1
    77 * Requires at least: 5.8
    88 * Requires PHP:      7.4
     
    9292    $settings_path = $base_dir . 'assets/js/askmyco-settings.js';
    9393
    94     $core_ver = file_exists($core_path) ? filemtime($core_path) : '0.8.0';
    95     $frontend_ver = file_exists($frontend_path) ? filemtime($frontend_path) : '0.8.0';
    96     $admin_init_ver = file_exists($admin_init_path) ? filemtime($admin_init_path) : '0.8.0';
    97     $style_ver = file_exists($style_path) ? filemtime($style_path) : '0.8.0';
    98     $settings_ver = file_exists($settings_path) ? filemtime($settings_path) : '0.8.0';
     94    $core_ver = file_exists($core_path) ? filemtime($core_path) : '0.8.1';
     95    $frontend_ver = file_exists($frontend_path) ? filemtime($frontend_path) : '0.8.1';
     96    $admin_init_ver = file_exists($admin_init_path) ? filemtime($admin_init_path) : '0.8.1';
     97    $style_ver = file_exists($style_path) ? filemtime($style_path) : '0.8.1';
     98    $settings_ver = file_exists($settings_path) ? filemtime($settings_path) : '0.8.1';
    9999
    100100    if (! wp_script_is($core_handle, 'registered')) {
     
    209209
    210210    $floating_path = plugin_dir_path(__FILE__) . 'assets/js/amc-floating.js';
    211     $floating_ver = file_exists($floating_path) ? filemtime($floating_path) : '0.8.0';
     211    $floating_ver = file_exists($floating_path) ? filemtime($floating_path) : '0.8.1';
    212212
    213213    wp_enqueue_script(
  • ask-my-content/trunk/assets/js/amc-admin-init.js

    r3445867 r3448236  
    4949})();
    5050
    51 // AMC admin counters: fetch and update UI, no inline scripts needed
    52 (function () {
    53     function amcUpdateCountersDisplay(d) {
    54         if (!d) return;
    55         var i = document.getElementById('amc-indexed-count');
    56         var ci = document.getElementById('amc-chat-in-count');
    57         var co = document.getElementById('amc-chat-out-count');
    58         if (typeof d.embed_count === 'number' && i) i.textContent = d.embed_count;
    59         if (typeof d.chat_in_count === 'number' && ci) ci.textContent = d.chat_in_count;
    60         if (typeof d.chat_out_count === 'number' && co) co.textContent = d.chat_out_count;
    61     }
    62 
    63     async function amcFetchCounters() {
    64         var statusEl = document.getElementById('amc-counters-status');
    65         if (statusEl) statusEl.textContent = 'Loading…';
    66         try {
    67             var ajaxUrl = (window.amcAdminChat && window.amcAdminChat.ajaxUrl) || (window.ajaxurl) || '/wp-admin/admin-ajax.php';
    68             var body = new URLSearchParams();
    69             body.set('action', 'askmyco_get_counters');
    70             if (window.amcAdminChat && window.amcAdminChat.nonce) {
    71                 body.set('security', window.amcAdminChat.nonce);
    72             }
    73             const res = await fetch(ajaxUrl, {
    74                 method: 'POST',
    75                 credentials: 'same-origin',
    76                 headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
    77                 body: body.toString()
    78             });
    79             var data = await res.json();
    80             if (data && data.success) {
    81                 amcUpdateCountersDisplay(data.data);
    82                 if (statusEl) statusEl.textContent = '';
    83             } else {
    84                 if (statusEl) {
    85                     var msg = (data && data.data && data.data.message) ? data.data.message : (data && data.message) ? data.message : 'Failed to load';
    86                     statusEl.textContent = msg;
    87                 }
    88             }
    89         } catch (err) {
    90             if (statusEl) statusEl.textContent = (err && err.message) ? err.message : 'Error';
    91         }
    92     }
    93 
    94     function amcScheduleFinishedCheck() {
    95         setTimeout(function () {
    96             var s = document.getElementById('amc-status');
    97             var text = (s && s.textContent) ? s.textContent.toLowerCase() : '';
    98             if (text.includes('finished indexing')) amcFetchCounters();
    99         }, 1500);
    100     }
    101 
    102     // Wire button
    103     var btn = document.getElementById('amc-refresh-counters');
    104     if (btn) btn.addEventListener('click', amcFetchCounters);
    105 
    106     // Initial load
    107     if (document.readyState === 'loading') {
    108         document.addEventListener('DOMContentLoaded', function () {
    109             amcFetchCounters();
    110             amcScheduleFinishedCheck();
    111         });
    112     } else {
    113         amcFetchCounters();
    114         amcScheduleFinishedCheck();
    115     }
    116 
    117     // Refresh after chat answer event
    118     window.addEventListener('amc-chat-answered', function () { amcFetchCounters(); });
    119 })();
    120 
    12151// Enable Save Changes only when settings fields actually change
    12252(function () {
  • ask-my-content/trunk/build/ask-my-content/block.json

    r3445867 r3448236  
    33  "apiVersion": 3,
    44  "name": "amc/ask-my-content",
    5   "version": "0.8.0",
     5  "version": "0.8.1",
    66  "title": "Ask My Content",
    77  "category": "widgets",
  • ask-my-content/trunk/build/blocks-manifest.php

    r3445867 r3448236  
    66        'apiVersion' => 3,
    77        'name' => 'amc/ask-my-content',
    8         'version' => '0.8.0',
     8        'version' => '0.8.1',
    99        'title' => 'Ask My Content',
    1010        'category' => 'widgets',
  • ask-my-content/trunk/includes/activate.php

    r3440656 r3448236  
    7373    askmyco_debug_log('Activate site_url: ' . $site_url, 'debug');
    7474
    75     // Capture the best-available email for the activating admin/user.
    76     $activator_email = '';
    77     if (function_exists('wp_get_current_user')) {
    78         $user = wp_get_current_user();
    79         if ($user instanceof WP_User && $user->exists()) {
    80             $activator_email = sanitize_email($user->user_email);
    81         }
    82     }
    83     if ($activator_email === '') {
    84         $activator_email = sanitize_email((string) get_option('admin_email', ''));
    85     }
     75    $activator_email = askmyco_get_admin_email();
    8676
    8777    // Fire-and-forget notification to backend about activation
    8878    if (function_exists('askmyco_send_content_to_backend')) {
    8979        // Endpoint will resolve to <base>/_api_/plugin; payload: { siteId, data: { domain, action } }
    90         askmyco_debug_log(
    91             'Activation payload: domain=' . $domain . ' action=activated email=' . $activator_email,
    92             'debug'
    93         );
    9480        askmyco_send_content_to_backend([
    9581            'domain' => $domain,
     
    10692    askmyco_debug_log('Activation finished', 'info');
    10793}
     94
     95/**
     96 * Get the best-available admin email (current user if available, otherwise site admin email).
     97 *
     98 * @return string Sanitized email address.
     99 */
     100function askmyco_get_admin_email(): string
     101{
     102    $email = '';
     103    if (function_exists('wp_get_current_user')) {
     104        $user = wp_get_current_user();
     105        if ($user instanceof WP_User && $user->exists()) {
     106            $email = sanitize_email($user->user_email);
     107        }
     108    }
     109    if ($email === '') {
     110        $email = sanitize_email((string) get_option('admin_email', ''));
     111    }
     112    return $email;
     113}
     114
     115/**
     116 * Handle plugin update notification via upgrader_process_complete hook.
     117 *
     118 * @param WP_Upgrader $upgrader Upgrader instance.
     119 * @param array       $options  Array of bulk update data.
     120 */
     121function askmyco_on_plugin_update($upgrader, $options)
     122{
     123    // Debug: log every call to see if hook fires and with what params
     124    askmyco_debug_log('upgrader_process_complete hook fired', 'info');
     125    askmyco_debug_log('options: ' . wp_json_encode($options), 'info');
     126
     127    // Only handle plugin updates
     128    if (!isset($options['type']) || $options['type'] !== 'plugin') {
     129        askmyco_debug_log('Skipping: type is not plugin', 'debug');
     130        return;
     131    }
     132    // Handle both 'update' action and 'install' action (zip uploads are reported as install)
     133    $action = isset($options['action']) ? $options['action'] : '';
     134    if ($action !== 'update' && $action !== 'install') {
     135        askmyco_debug_log('Skipping: action is not update or install', 'debug');
     136        return;
     137    }
     138    // Check if our plugin is in the list of updated plugins
     139    $our_plugin = 'ask-my-content/ask-my-content.php';
     140    $plugins = [];
     141    if (isset($options['plugins']) && is_array($options['plugins'])) {
     142        $plugins = $options['plugins'];
     143    } elseif (isset($options['plugin'])) {
     144        $plugins = [$options['plugin']];
     145    }
     146    // For install action, check the upgrader result for plugin info
     147    if (empty($plugins) && $action === 'install' && $upgrader instanceof Plugin_Upgrader) {
     148        $result = $upgrader->result;
     149        if (is_array($result) && isset($result['destination_name'])) {
     150            $dest = $result['destination_name'];
     151            askmyco_debug_log('Install destination_name: ' . $dest, 'debug');
     152            if ($dest === 'ask-my-content') {
     153                $plugins = [$our_plugin];
     154            }
     155        }
     156    }
     157    askmyco_debug_log('Looking for ' . $our_plugin . ' in: ' . wp_json_encode($plugins), 'debug');
     158
     159    if (!in_array($our_plugin, $plugins, true)) {
     160        askmyco_debug_log('Skipping: our plugin not in list', 'debug');
     161        return;
     162    }
     163    // For 'install' action, this could be a fresh install or an overwrite (update via zip)
     164    // We only want to send 'updated' for overwrites, not fresh installs
     165    // Check if we had a stored UUID before (indicates plugin was previously installed)
     166    $site_uuid = get_option('askmyco_site_uuid');
     167    if ($action === 'install' && !$site_uuid) {
     168        askmyco_debug_log('Skipping: fresh install (no existing UUID)', 'debug');
     169        return;
     170    }
     171    askmyco_debug_log('Plugin update detected via ' . $action . ' action', 'info');
     172
     173    $parsed = wp_parse_url(home_url());
     174    $domain = isset($parsed['host']) ? $parsed['host'] : '';
     175    $admin_email = askmyco_get_admin_email();
     176
     177    // Fire-and-forget notification to backend about update
     178    if (function_exists('askmyco_send_content_to_backend')) {
     179        askmyco_send_content_to_backend([
     180            'domain' => $domain,
     181            'action' => 'updated',
     182            'email' => $admin_email,
     183        ], 'plugin', 'non_blocking');
     184        askmyco_debug_log(
     185            'Update ping sent (non-blocking) domain=' . $domain . ' action=updated email=' . $admin_email,
     186            'info'
     187        );
     188    } else {
     189        askmyco_debug_log('askmyco_send_content_to_backend not available during update', 'warn');
     190    }
     191}
     192add_action('upgrader_process_complete', 'askmyco_on_plugin_update', 10, 2);
  • ask-my-content/trunk/includes/cli.php

    r3384523 r3448236  
    6262         * : Include posts in addition to pages.
    6363         *
     64         * [--pages-only]
     65         * : Index pages only (exclude posts). Overrides any previously saved setting.
     66         *
    6467         * [--force]
    65          * : Steal an existing lock and run anyway.
     68         * : Steal an existing lock and run anyway (use with caution if another process may be running).
    6669         *
    6770         * ## EXAMPLES
    6871         *     wp amc index
    6972         *     wp amc index --include-posts
     73         *     wp amc index --pages-only
    7074         *     wp amc index --force
    7175         */
     
    7377        {
    7478            $include_posts = isset($assoc_args['include-posts']);
     79            $pages_only    = isset($assoc_args['pages-only']);
    7580            $force         = isset($assoc_args['force']);
    7681
     82            // Explicit control: --include-posts or --pages-only set the option for this run only
     83            if ($include_posts && $pages_only) {
     84                WP_CLI::error('Cannot use both --include-posts and --pages-only.');
     85            }
    7786            if ($include_posts) {
    7887                update_option('askmyco_include_posts', '1');
     88            } elseif ($pages_only) {
     89                update_option('askmyco_include_posts', '0');
    7990            }
     91            // If neither flag given, use existing saved setting
    8092
    8193            // Handle existing lock
    8294            if (get_option('askmyco_indexing_lock')) {
    8395                if ($force) {
    84                     WP_CLI::warning('Existing lock found. Forcing takeover.');
     96                    WP_CLI::warning('Existing lock found. Forcing takeover (ensure no other process is running).');
    8597                    delete_option('askmyco_indexing_lock');
    8698                } else {
     
    88100                }
    89101            }
     102            askmyco_db_clear_stop_requested();
    90103
    91             askmyco_db_clear_stop_requested();
    92104            update_option('askmyco_embedding_status', 'starting (CLI)…');
    93105
     
    96108                WP_CLI::error('Indexing function not available.');
    97109            }
    98 
    99             WP_CLI::log('Starting indexing…'); // phpcs:ignore
     110            $posts_mode = get_option('askmyco_include_posts', '0') === '1' ? 'pages+posts' : 'pages only';
     111            WP_CLI::log("Starting indexing ({$posts_mode})…"); // phpcs:ignore
    100112            $start = microtime(true);
    101113            askmyco_cron_index_content();
     
    103115
    104116            $status = get_option('askmyco_embedding_status', 'unknown');
    105             WP_CLI::success("Done ({$elapsed}s). Status: $status"); // phpcs:ignore
     117            $status_lower = strtolower($status);
     118
     119            // Determine exit status based on result
     120            if (strpos($status_lower, 'error') !== false || strpos($status_lower, 'failed') !== false) {
     121                WP_CLI::error("Indexing failed ({$elapsed}s). Status: $status");
     122            } elseif (strpos($status_lower, 'stopped') !== false || strpos($status_lower, 'stopping') !== false) {
     123                WP_CLI::warning("Indexing stopped ({$elapsed}s). Status: $status"); // phpcs:ignore
     124            } else {
     125                WP_CLI::success("Done ({$elapsed}s). Status: $status"); // phpcs:ignore
     126            }
    106127        }
    107 
    108128        /**
    109129         * Show current indexing status and counters.
     
    143163            WP_CLI::log(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); // phpcs:ignore
    144164        }
    145 
    146165        /**
    147166         * Request a stop for a running indexing process.
     
    157176        }
    158177    }
    159 
    160178    WP_CLI::add_command('amc', 'ASKMYCO_CLI_Command'); // phpcs:ignore
    161179}
  • ask-my-content/trunk/includes/settings_embed.php

    r3414721 r3448236  
    66
    77// Settings Embed UI
     8
     9// Normalize stale indexing state: if is_indexing='1' but no lock and no scheduled cron, reset to idle
     10$askmyco_is_indexing_flag = get_option('askmyco_is_indexing', '0') === '1';
     11$askmyco_has_lock = (bool) get_option('askmyco_indexing_lock');
     12$askmyco_has_scheduled = (bool) wp_next_scheduled(ASKMYCO_CRON_HOOK);
     13
     14if ($askmyco_is_indexing_flag && !$askmyco_has_lock && !$askmyco_has_scheduled) {
     15    // Indexing was marked as running but nothing is actually happening - reset
     16    update_option('askmyco_is_indexing', '0');
     17    askmyco_db_clear_stop_requested();
     18    $askmyco_current_status = get_option('askmyco_embedding_status', '');
     19    // Only update status if it looks stale (scheduled/stopping but never ran)
     20    if (in_array($askmyco_current_status, ['scheduled…', 'stopping…', 'stopping...'], true)) {
     21        update_option('askmyco_embedding_status', 'reset (stale state detected)');
     22    }
     23}
    824
    925// Fetch counts and stats
     
    4561                // schedule shortly to reduce immediate race with other requests
    4662                wp_schedule_single_event(time() + 1, ASKMYCO_CRON_HOOK);
     63            }            // Encourage immediate cron execution (helps with aggressive caching plugins)
     64            if (function_exists('spawn_cron')) {
     65                spawn_cron();
    4766            }
    4867        }
  • ask-my-content/trunk/readme.txt

    r3445867 r3448236  
    55Requires PHP: 7.4
    66Tested up to: 6.9
    7 Stable tag: 0.8.0
     7Stable tag: 0.8.1
    88License: GPL-2.0-or-later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    28281. After activating the plugin, go to **Dashboard → Ask My Content** and press “Start Indexing” (or run `wp amc index`) to send your site content to the backend. Indexing must be completed before the chat can answer questions.
    29292. Enable the floating launcher to show the chat site-wide; or add the **Ask My Content** block to an existing or new page (or place the `[ask_my_content]` shortcode) and publish/update the page so the chat interface has a front-end location.
    30 3. Content is converted into embeddings (vector representations).
    31 4. When a visitor asks a question, the chatbot retrieves the most relevant content snippets from your indexed pages.
    32 5. OpenAI generates an answer based on that content.
     303. When a visitor asks a question, the chatbot retrieves the most relevant content snippets from your indexed pages.
     314. OpenAI generates an answer based on that content.
    3332
    3433After you edit content, rerun indexing from **Dashboard → Ask My Content** or via `wp amc index` to push the latest changes. Deleted posts are sent to the backend automatically when they are trashed or removed.
     
    4847
    4948== Frequently Asked Questions ==
     49
     50= Why is indexing stuck? =
     51The admin "Start Indexing" button schedules a background task via WordPress cron. If your site uses aggressive caching (e.g., LiteSpeed Cache, WP Super Cache), cached pages may bypass cron triggers, causing indexing to appear stuck at "scheduled…" or "stopping…".
     52
     53**Solutions:**
     541. Refresh the settings page — stale state is auto-detected and reset.
     552. Use `wp amc index` from the command line — it runs synchronously and doesn't depend on cron. See **Command Line Usage** below.
    5056
    5157= Does this plugin use my OpenAI API key? =
     
    7379* Use the settings page for a guided UI, manual start/stop buttons, and real-time counters inside the WordPress dashboard.
    7480* Use `wp amc` when you need automation (cron jobs, SSH sessions, CI) or want to run indexing without opening a browser. Available commands include:
    75   * `wp amc index [--include-posts] [--force]`
     81  * `wp amc index [--include-posts] [--pages-only] [--force]`
    7682  * `wp amc status`
    7783  * `wp amc stop`
     84
     85Run `wp help amc index` for full option descriptions:
     86
     87`
     88NAME
     89
     90  wp amc index
     91
     92DESCRIPTION
     93
     94  Run indexing immediately (pages always, posts optional).
     95
     96OPTIONS
     97
     98  [--include-posts]
     99    Include posts in addition to pages.
     100
     101  [--pages-only]
     102    Index pages only (exclude posts). Overrides any previously saved setting.
     103
     104  [--force]
     105    Steal an existing lock and run anyway (use with caution if another process may be running).
     106
     107EXAMPLES
     108
     109    wp amc index
     110    wp amc index --include-posts
     111    wp amc index --pages-only
     112    wp amc index --force
     113`
    78114
    79115= Installing WP-CLI =
     
    115151== Changelog ==
    116152
     153= 0.8.1 =
     154Amended WP-CLI command suite (`wp amc`)
     155
    117156= 0.8.0 =
    118157Added optional floating chat launcher that can appear site-wide.
  • ask-my-content/trunk/src/ask-my-content/block.json

    r3445867 r3448236  
    33    "apiVersion": 3,
    44    "name": "amc/ask-my-content",
    5     "version": "0.8.0",
     5    "version": "0.8.1",
    66    "title": "Ask My Content",
    77    "category": "widgets",
Note: See TracChangeset for help on using the changeset viewer.