Plugin Directory

Changeset 3486701


Ignore:
Timestamp:
03/19/2026 05:07:02 PM (2 weeks ago)
Author:
intufind
Message:

Release intufind v1.4.1

Location:
intufind
Files:
36 added
9 edited

Legend:

Unmodified
Added
Removed
  • intufind/trunk/admin/css/intufind-admin.css

    r3463908 r3486701  
    16081608}
    16091609
    1610 .wp-list-table th.column-intufind_searchable,
    1611 .wp-list-table td.column-intufind_searchable {
    1612   width: 40px !important;
     1610.wp-list-table th.column-intufind_index_status,
     1611.wp-list-table td.column-intufind_index_status {
     1612  width: 90px !important;
    16131613  text-align: center;
    16141614}
    16151615
    16161616.wp-list-table th.column-intufind_sync,
    1617 .wp-list-table th.column-intufind_searchable {
     1617.wp-list-table th.column-intufind_index_status {
    16181618  padding: 8px 4px;
    16191619}
     
    16351635}
    16361636
    1637 .wp-list-table th.column-intufind_searchable {
     1637.wp-list-table th.column-intufind_index_status {
    16381638  cursor: help;
    16391639}
     
    16591659/* Align cell content with other columns (top) */
    16601660.wp-list-table td.column-intufind_sync,
    1661 .wp-list-table td.column-intufind_searchable {
     1661.wp-list-table td.column-intufind_index_status {
    16621662  vertical-align: top;
    16631663  padding-top: 12px;
     
    16651665  padding-left: 4px;
    16661666  padding-right: 4px;
     1667}
     1668
     1669/* Index status select in list tables */
     1670.intufind-index-status-select {
     1671  font-size: 12px;
     1672  padding: 2px 4px;
     1673  min-width: 70px;
     1674  max-width: 85px;
     1675  height: auto;
     1676  line-height: 1.4;
    16671677}
    16681678
     
    21342144  /* Hide Intufind columns on small screens */
    21352145  .wp-list-table .column-intufind_sync,
    2136   .wp-list-table .column-intufind_searchable {
     2146  .wp-list-table .column-intufind_index_status {
    21372147    display: none;
    21382148  }
  • intufind/trunk/admin/js/intufind-admin.js

    r3461186 r3486701  
    12131213    /**
    12141214     * Intufind List Columns module.
    1215      * Handles sync/searchable toggles in post list tables.
     1215     * Handles sync/index-status toggles in post list tables.
    12161216     */
    12171217    const IntufindListColumns = {
     
    12281228        bindEvents: function () {
    12291229            $(document).on('change', '.intufind-sync-toggle', this.handleSyncToggle.bind(this));
    1230             $(document).on('change', '.intufind-searchable-toggle', this.handleSearchableToggle.bind(this));
     1230            $(document).on('change', '.intufind-index-status-select', this.handleIndexStatusChange.bind(this));
    12311231        },
    12321232
     
    12541254                success: (response) => {
    12551255                    if (response.success) {
    1256                         // Find the searchable column container (by column class, not by checkbox).
    1257                         const $searchableContainer = $row.find('.column-intufind_searchable .intufind-col-toggle');
     1256                        const $indexStatusContainer = $row.find('.column-intufind_index_status .intufind-col-toggle');
    12581257
    12591258                        if (response.data.synced) {
     
    12771276                            }
    12781277
    1279                             // Re-enable searchable toggle if it was disabled.
    1280                             if ($searchableContainer.length && $searchableContainer.hasClass('intufind-col-toggle--disabled') && response.data.syncStatus === 'success') {
    1281                                 // Restore searchable toggle (default to checked/searchable for published posts).
    1282                                 const searchableNonce = response.data.searchableNonce || '';
    1283                                 $searchableContainer
     1278                            // Re-enable index status select if it was disabled.
     1279                            if ($indexStatusContainer.length && $indexStatusContainer.hasClass('intufind-col-toggle--disabled') && response.data.syncStatus === 'success') {
     1280                                const indexStatusNonce = response.data.indexStatusNonce || '';
     1281                                $indexStatusContainer
    12841282                                    .removeClass('intufind-col-toggle--disabled')
    12851283                                    .removeAttr('title')
    12861284                                    .html(`
    1287                                         <label class="intufind-mini-toggle">
    1288                                             <input type="checkbox" class="intufind-searchable-toggle" data-post-id="${postId}" data-nonce="${searchableNonce}" checked />
    1289                                             <span class="intufind-mini-toggle__slider"></span>
    1290                                         </label>
     1285                                        <select class="intufind-index-status-select" data-post-id="${postId}" data-nonce="${indexStatusNonce}">
     1286                                            <option value="active" selected>Active</option>
     1287                                            <option value="hidden">Hidden</option>
     1288                                            <option value="disabled">Disabled</option>
     1289                                        </select>
    12911290                                    `);
    12921291                            }
    12931292                        } else {
    1294                             // Sync disabled - remove status and disable searchable.
     1293                            // Sync disabled - remove status and disable index status.
    12951294                            $container.find('.intufind-col-status').remove();
    1296                             if ($searchableContainer.length) {
    1297                                 $searchableContainer
     1295                            if ($indexStatusContainer.length) {
     1296                                $indexStatusContainer
    12981297                                    .html('<span class="intufind-col-dash">&mdash;</span>')
    12991298                                    .addClass('intufind-col-toggle--disabled')
     
    13191318
    13201319        /**
    1321          * Handle searchable toggle change.
    1322          */
    1323         handleSearchableToggle: (e) => {
    1324             const $checkbox = $(e.currentTarget);
    1325             const postId = $checkbox.data('post-id');
    1326             const nonce = $checkbox.data('nonce');
    1327             const $container = $checkbox.closest('.intufind-col-toggle');
    1328 
     1320         * Handle index status select change.
     1321         */
     1322        handleIndexStatusChange: (e) => {
     1323            const $select = $(e.currentTarget);
     1324            const postId = $select.data('post-id');
     1325            const nonce = $select.data('nonce');
     1326            const newStatus = $select.val();
     1327            const previousStatus = $select.data('previous-value') || $select.find('option:first').val();
     1328            const $container = $select.closest('.intufind-col-toggle');
     1329
     1330            $select.data('previous-value', newStatus);
    13291331            $container.addClass('is-loading');
    1330             $checkbox.prop('disabled', true);
     1332            $select.prop('disabled', true);
    13311333
    13321334            $.ajax({
     
    13341336                type: 'POST',
    13351337                data: {
    1336                     action: 'intufind_toggle_searchable',
     1338                    action: 'intufind_toggle_index_status',
    13371339                    post_id: postId,
    13381340                    nonce: nonce,
     1341                    status: newStatus,
    13391342                },
    13401343                success: (response) => {
    13411344                    if (!response.success) {
    1342                         $checkbox.prop('checked', !$checkbox.prop('checked'));
    1343                         alert(response.data.message || 'Error toggling searchable.');
     1345                        $select.val(previousStatus);
     1346                        alert(response.data.message || 'Error updating index status.');
    13441347                    }
    13451348                },
    13461349                error: () => {
    1347                     $checkbox.prop('checked', !$checkbox.prop('checked'));
     1350                    $select.val(previousStatus);
    13481351                    alert('Request failed.');
    13491352                },
    13501353                complete: () => {
    13511354                    $container.removeClass('is-loading');
    1352                     $checkbox.prop('disabled', false);
     1355                    $select.prop('disabled', false);
    13531356                },
    13541357            });
  • intufind/trunk/admin/partials/search-display.php

    r3463908 r3486701  
    390390
    391391                            <div class="intufind-info-section">
    392                                 <h4><?php esc_html_e( 'Searchable Content', 'intufind' ); ?></h4>
     392                                <h4><?php esc_html_e( 'Status', 'intufind' ); ?></h4>
    393393                                <p>
    394                                     <?php esc_html_e( 'Only synced content marked as "Searchable" appears in search results. Use the Knowledge page and post list columns to control what\'s searchable.', 'intufind' ); ?>
     394                                    <?php esc_html_e( 'Only synced content with an "Active" index status appears in search results. Use the Knowledge page and post list columns to control visibility.', 'intufind' ); ?>
    395395                                </p>
    396396                            </div>
  • intufind/trunk/includes/class-intufind-content-extractor.php

    r3484551 r3486701  
    117117            'tags'          => $tags,
    118118            'featuredImage' => $image_url,
    119             'searchable'    => Intufind_List_Columns::is_searchable( $post_id ),
     119            'indexStatus'   => Intufind_List_Columns::get_index_status( $post_id ),
    120120            'source'        => 'wordpress',
    121121        );
     
    289289            // Visibility.
    290290            'catalogVisibility' => $product->get_catalog_visibility(),
    291             'searchable'      => Intufind_List_Columns::is_searchable( $product_id ) && 'hidden' !== $product->get_catalog_visibility(),
     291            'indexStatus'     => 'hidden' === $product->get_catalog_visibility() ? 'hidden' : Intufind_List_Columns::get_index_status( $product_id ),
    292292
    293293            // Source tracking.
  • intufind/trunk/includes/class-intufind-exclusions.php

    r3461186 r3486701  
    432432        }
    433433
    434         // Private posts can be synced (they'll be marked as non-searchable/chatbot only).
     434        // Private posts can be synced (they'll default to 'hidden' index status/chatbot only).
    435435        if ( 'private' === $status ) {
    436436            return true;
  • intufind/trunk/includes/class-intufind-list-columns.php

    r3461186 r3486701  
    33 * List Columns functionality.
    44 *
    5  * Adds sync and searchable columns to WordPress post list tables
     5 * Adds sync and index status columns to WordPress post list tables
    66 * for granular control over AI indexing.
    77 *
     
    2323     */
    2424    const META_EXCLUDE_SYNC = '_intufind_exclude_sync';
    25     const META_SEARCHABLE   = '_intufind_searchable';
     25    const META_INDEX_STATUS = '_intufind_index_status';
    2626
    2727    /**
     
    8080        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) );
    8181        add_action( 'wp_ajax_intufind_toggle_sync', array( $this, 'ajax_toggle_sync' ) );
    82         add_action( 'wp_ajax_intufind_toggle_searchable', array( $this, 'ajax_toggle_searchable' ) );
     82        add_action( 'wp_ajax_intufind_toggle_index_status', array( $this, 'ajax_toggle_index_status' ) );
    8383    }
    8484
     
    165165        // Column headers with WordPress-style icon markup and tooltips.
    166166        $sync_title       = __( 'Intufind: AI Sync', 'intufind' );
    167         $searchable_title = __( 'Intufind: Searchable', 'intufind' );
     167        $index_status_title = __( 'Intufind: Status', 'intufind' );
    168168
    169169        $sync_header = sprintf(
     
    172172            esc_html( $sync_title )
    173173        );
    174         $searchable_header = sprintf(
     174        $index_status_header = sprintf(
    175175            '<span class="intufind-col-header dashicons dashicons-visibility" aria-hidden="true" title="%s"></span><span class="screen-reader-text">%s</span>',
    176             esc_attr( $searchable_title ),
    177             esc_html( $searchable_title )
     176            esc_attr( $index_status_title ),
     177            esc_html( $index_status_title )
    178178        );
    179179
     
    181181            // Insert before date column.
    182182            if ( 'date' === $key ) {
    183                 $new_columns['intufind_sync']       = $sync_header;
    184                 $new_columns['intufind_searchable'] = $searchable_header;
     183                $new_columns['intufind_sync']         = $sync_header;
     184                $new_columns['intufind_index_status'] = $index_status_header;
    185185            }
    186186            $new_columns[ $key ] = $label;
     
    189189        // If date doesn't exist, add at end.
    190190        if ( ! isset( $new_columns['intufind_sync'] ) ) {
    191             $new_columns['intufind_sync']       = $sync_header;
    192             $new_columns['intufind_searchable'] = $searchable_header;
     191            $new_columns['intufind_sync']         = $sync_header;
     192            $new_columns['intufind_index_status'] = $index_status_header;
    193193        }
    194194
     
    205205        if ( 'intufind_sync' === $column ) {
    206206            $this->render_sync_column( $post_id );
    207         } elseif ( 'intufind_searchable' === $column ) {
    208             $this->render_searchable_column( $post_id );
     207        } elseif ( 'intufind_index_status' === $column ) {
     208            $this->render_index_status_column( $post_id );
    209209        }
    210210    }
     
    304304
    305305    /**
    306      * Render searchable column.
     306     * Render index status column.
    307307     *
    308308     * @param int $post_id Post ID.
    309309     */
    310     private function render_searchable_column( $post_id ) {
     310    private function render_index_status_column( $post_id ) {
    311311        $post = get_post( $post_id );
    312312        if ( ! $post ) {
     
    316316        // Not a syncable status (drafts, etc.).
    317317        if ( ! in_array( $post->post_status, array( 'publish', 'private' ), true ) ) {
    318             $this->render_disabled_toggle( 'searchable', __( 'Publish first', 'intufind' ) );
    319             return;
    320         }
    321 
    322         // System-excluded posts can't be synced, so searchable doesn't apply.
     318            $this->render_disabled_toggle( 'index_status', __( 'Publish first', 'intufind' ) );
     319            return;
     320        }
     321
     322        // System-excluded posts can't be synced, so index status doesn't apply.
    323323        if ( $this->is_system_excluded( $post_id, $post ) ) {
    324             $this->render_disabled_toggle( 'searchable', __( 'Cannot be synced', 'intufind' ) );
    325             return;
    326         }
    327 
    328         // Check if synced - searchable only applies to synced content.
     324            $this->render_disabled_toggle( 'index_status', __( 'Cannot be synced', 'intufind' ) );
     325            return;
     326        }
     327
     328        // Check if synced - index status only applies to synced content.
    329329        if ( ! self::is_synced( $post_id ) ) {
    330             $this->render_disabled_toggle( 'searchable', __( 'Enable sync first', 'intufind' ) );
    331             return;
    332         }
    333 
    334         // Get searchable setting.
    335         // Default: published = searchable, private = not searchable (chatbot only).
    336         $searchable_meta = get_post_meta( $post_id, self::META_SEARCHABLE, true );
    337         if ( '' === $searchable_meta ) {
    338             // No explicit setting - use defaults.
    339             $is_searchable = 'publish' === $post->post_status;
    340         } else {
    341             $is_searchable = 'no' !== $searchable_meta;
    342         }
    343 
    344         $nonce = wp_create_nonce( 'intufind_toggle_searchable_' . $post_id );
     330            $this->render_disabled_toggle( 'index_status', __( 'Enable sync first', 'intufind' ) );
     331            return;
     332        }
     333
     334        $current_status = self::get_index_status( $post_id );
     335        $nonce          = wp_create_nonce( 'intufind_toggle_index_status_' . $post_id );
     336
     337        $statuses = array(
     338            'active'   => __( 'Active', 'intufind' ),
     339            'hidden'   => __( 'Hidden', 'intufind' ),
     340            'disabled' => __( 'Disabled', 'intufind' ),
     341        );
    345342
    346343        ?>
    347344        <div class="intufind-col-toggle" data-post-id="<?php echo esc_attr( $post_id ); ?>">
    348             <label class="intufind-mini-toggle">
    349                 <input
    350                     type="checkbox"
    351                     class="intufind-searchable-toggle"
    352                     data-post-id="<?php echo esc_attr( $post_id ); ?>"
    353                     data-nonce="<?php echo esc_attr( $nonce ); ?>"
    354                     <?php checked( $is_searchable ); ?>
    355                 />
    356                 <span class="intufind-mini-toggle__slider"></span>
    357             </label>
     345            <select
     346                class="intufind-index-status-select"
     347                data-post-id="<?php echo esc_attr( $post_id ); ?>"
     348                data-nonce="<?php echo esc_attr( $nonce ); ?>"
     349            >
     350                <?php foreach ( $statuses as $value => $label ) : ?>
     351                    <option value="<?php echo esc_attr( $value ); ?>" <?php selected( $current_status, $value ); ?>>
     352                        <?php echo esc_html( $label ); ?>
     353                    </option>
     354                <?php endforeach; ?>
     355            </select>
    358356        </div>
    359357        <?php
     
    440438     */
    441439    public function add_bulk_actions( $actions ) {
    442         $actions['intufind_enable_sync']    = __( 'Enable AI Sync', 'intufind' );
    443         $actions['intufind_disable_sync']   = __( 'Disable AI Sync', 'intufind' );
    444         $actions['intufind_make_searchable'] = __( 'Make Searchable', 'intufind' );
    445         $actions['intufind_hide_search']    = __( 'Hide from Search', 'intufind' );
     440        $actions['intufind_enable_sync']      = __( 'Enable AI Sync', 'intufind' );
     441        $actions['intufind_disable_sync']     = __( 'Disable AI Sync', 'intufind' );
     442        $actions['intufind_index_active']     = __( 'Set Index: Active', 'intufind' );
     443        $actions['intufind_index_hidden']     = __( 'Set Index: Hidden', 'intufind' );
     444        $actions['intufind_index_disabled']   = __( 'Set Index: Disabled', 'intufind' );
    446445
    447446        return $actions;
     
    460459            'intufind_enable_sync',
    461460            'intufind_disable_sync',
    462             'intufind_make_searchable',
    463             'intufind_hide_search',
     461            'intufind_index_active',
     462            'intufind_index_hidden',
     463            'intufind_index_disabled',
    464464        );
    465465
     
    483483
    484484                    if ( $document ) {
    485                         $document['searchable'] = self::is_searchable( $post_id );
     485                        $document['indexStatus'] = self::get_index_status( $post_id );
    486486
    487487                        if ( 'product' === $post_type ) {
     
    519519                    break;
    520520
    521                 case 'intufind_make_searchable':
    522                     update_post_meta( $post_id, self::META_SEARCHABLE, 'yes' );
    523                     $count++;
    524                     break;
    525 
    526                 case 'intufind_hide_search':
    527                     update_post_meta( $post_id, self::META_SEARCHABLE, 'no' );
     521                case 'intufind_index_active':
     522                case 'intufind_index_hidden':
     523                case 'intufind_index_disabled':
     524                    $status_map = array(
     525                        'intufind_index_active'   => 'active',
     526                        'intufind_index_hidden'   => 'hidden',
     527                        'intufind_index_disabled' => 'disabled',
     528                    );
     529                    $new_idx_status = $status_map[ $action ];
     530
     531                    update_post_meta( $post_id, self::META_INDEX_STATUS, $new_idx_status );
     532
     533                    if ( self::is_synced( $post_id ) ) {
     534                        $post      = get_post( $post_id );
     535                        $post_type = $post ? $post->post_type : 'post';
     536                        $document  = $this->extractor->extract( $post_id, $post_type );
     537
     538                        if ( $document ) {
     539                            $document['indexStatus'] = $new_idx_status;
     540
     541                            if ( 'product' === $post_type ) {
     542                                $upsert_result = $this->api->upsert_products( array( $document ) );
     543                            } else {
     544                                $upsert_result = $this->api->upsert_posts( array( $document ) );
     545                            }
     546
     547                            if ( ! is_wp_error( $upsert_result ) ) {
     548                                $this->status->mark_synced( $post_id, '', 'manual' );
     549                            } else {
     550                                $this->status->mark_error( $post_id, $upsert_result->get_error_message() );
     551                            }
     552                        }
     553                    }
     554
    528555                    $count++;
    529556                    break;
     
    601628                $count
    602629            ),
    603             'intufind_make_searchable' => sprintf(
     630            'intufind_index_active'  => sprintf(
    604631                /* translators: %d: number of posts */
    605                 _n( '%d item made searchable.', '%d items made searchable.', $count, 'intufind' ),
     632                _n( '%d item set to active.', '%d items set to active.', $count, 'intufind' ),
    606633                $count
    607634            ),
    608             'intufind_hide_search'    => sprintf(
     635            'intufind_index_hidden'   => sprintf(
    609636                /* translators: %d: number of posts */
    610                 _n( '%d item hidden from search.', '%d items hidden from search.', $count, 'intufind' ),
     637                _n( '%d item set to hidden.', '%d items set to hidden.', $count, 'intufind' ),
     638                $count
     639            ),
     640            'intufind_index_disabled' => sprintf(
     641                /* translators: %d: number of posts */
     642                _n( '%d item set to disabled.', '%d items set to disabled.', $count, 'intufind' ),
    611643                $count
    612644            ),
     
    696728
    697729            if ( $document ) {
    698                 $document['searchable'] = self::is_searchable( $post_id );
     730                $document['indexStatus'] = self::get_index_status( $post_id );
    699731
    700732                if ( 'product' === $post_type ) {
     
    719751        );
    720752
    721         // Include searchable nonce when enabling sync so JS can restore the toggle.
     753        // Include index status nonce when enabling sync so JS can restore the select.
    722754        if ( $is_synced ) {
    723             $response['searchableNonce'] = wp_create_nonce( 'intufind_toggle_searchable_' . $post_id );
     755            $response['indexStatusNonce'] = wp_create_nonce( 'intufind_toggle_index_status_' . $post_id );
    724756        }
    725757
     
    728760
    729761    /**
    730      * AJAX handler for toggling searchable.
    731      */
    732     public function ajax_toggle_searchable() {
    733         $post_id = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
    734         $nonce   = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
     762     * AJAX handler for setting index status.
     763     */
     764    public function ajax_toggle_index_status() {
     765        $post_id    = isset( $_POST['post_id'] ) ? intval( $_POST['post_id'] ) : 0;
     766        $nonce      = isset( $_POST['nonce'] ) ? sanitize_text_field( wp_unslash( $_POST['nonce'] ) ) : '';
     767        $new_status = isset( $_POST['status'] ) ? sanitize_text_field( wp_unslash( $_POST['status'] ) ) : '';
    735768
    736769        if ( ! $post_id ) {
     
    738771        }
    739772
    740         if ( ! wp_verify_nonce( $nonce, 'intufind_toggle_searchable_' . $post_id ) ) {
     773        if ( ! wp_verify_nonce( $nonce, 'intufind_toggle_index_status_' . $post_id ) ) {
    741774            wp_send_json_error( array( 'message' => __( 'Session expired. Please refresh the page and try again.', 'intufind' ) ) );
    742775        }
     
    746779        }
    747780
    748         // Get current effective state and toggle to opposite.
    749         $is_currently_searchable = self::is_searchable( $post_id );
    750 
    751         if ( $is_currently_searchable ) {
    752             // Was searchable, now hide.
    753             update_post_meta( $post_id, self::META_SEARCHABLE, 'no' );
    754             $is_searchable = false;
    755         } else {
    756             // Was hidden, now searchable.
    757             update_post_meta( $post_id, self::META_SEARCHABLE, 'yes' );
    758             $is_searchable = true;
    759         }
     781        $valid_statuses = array( 'active', 'hidden', 'disabled' );
     782        if ( ! in_array( $new_status, $valid_statuses, true ) ) {
     783            wp_send_json_error( array( 'message' => __( 'Invalid status.', 'intufind' ) ) );
     784        }
     785
     786        update_post_meta( $post_id, self::META_INDEX_STATUS, $new_status );
     787
     788        $post      = get_post( $post_id );
     789        $post_type = $post ? $post->post_type : 'post';
     790        $document  = $this->extractor->extract( $post_id, $post_type );
     791
     792        $sync_success = false;
     793        if ( $document ) {
     794            $document['indexStatus'] = $new_status;
     795
     796            if ( 'product' === $post_type ) {
     797                $result = $this->api->upsert_products( array( $document ) );
     798            } else {
     799                $result = $this->api->upsert_posts( array( $document ) );
     800            }
     801
     802            if ( ! is_wp_error( $result ) ) {
     803                $this->status->mark_synced( $post_id, '', 'manual' );
     804                $sync_success = true;
     805            } else {
     806                $this->status->mark_error( $post_id, $result->get_error_message() );
     807            }
     808        }
     809
     810        $labels = array(
     811            'active'   => __( 'Index status: Active', 'intufind' ),
     812            'hidden'   => __( 'Index status: Hidden', 'intufind' ),
     813            'disabled' => __( 'Index status: Disabled', 'intufind' ),
     814        );
    760815
    761816        wp_send_json_success(
    762817            array(
    763                 'searchable' => $is_searchable,
    764                 'message'    => $is_searchable ? __( 'Now searchable', 'intufind' ) : __( 'Hidden from search', 'intufind' ),
     818                'indexStatus' => $new_status,
     819                'syncStatus'  => $sync_success ? 'success' : 'error',
     820                'message'     => $labels[ $new_status ],
    765821            )
    766822        );
     
    806862
    807863    /**
    808      * Check if a post is searchable.
    809      *
    810      * Defaults: published = searchable, private = not searchable (chatbot only).
     864     * Get the index status of a post.
     865     *
     866     * Defaults: published = 'active', private = 'hidden'.
    811867     * User can override either default.
    812868     *
    813869     * @param int $post_id Post ID.
    814      * @return bool Whether searchable.
    815      */
    816     public static function is_searchable( $post_id ) {
    817         // Must be synced to be searchable.
     870     * @return string Index status: 'active', 'hidden', or 'disabled'.
     871     */
     872    public static function get_index_status( $post_id ) {
    818873        if ( ! self::is_synced( $post_id ) ) {
    819             return false;
     874            return 'disabled';
    820875        }
    821876
    822877        $post = get_post( $post_id );
    823878        if ( ! $post ) {
    824             return false;
    825         }
    826 
    827         $searchable_meta = get_post_meta( $post_id, self::META_SEARCHABLE, true );
    828 
    829         // Explicit setting overrides defaults.
    830         if ( 'no' === $searchable_meta ) {
    831             return false;
    832         }
    833         if ( 'yes' === $searchable_meta ) {
    834             return true;
     879            return 'disabled';
     880        }
     881
     882        $status_meta = get_post_meta( $post_id, self::META_INDEX_STATUS, true );
     883
     884        $valid_statuses = array( 'active', 'hidden', 'disabled' );
     885        if ( in_array( $status_meta, $valid_statuses, true ) ) {
     886            return $status_meta;
    835887        }
    836888
    837889        // Default based on post status.
    838         return 'publish' === $post->post_status;
     890        return 'publish' === $post->post_status ? 'active' : 'hidden';
    839891    }
    840892}
  • intufind/trunk/intufind.php

    r3484551 r3486701  
    44 * Plugin URI: https://intufind.com/integrations/wordpress
    55 * Description: AI-powered search and chat for WordPress. Syncs your content to the cloud for semantic search, intelligent recommendations, and conversational AI.
    6  * Version: 1.4.0
     6 * Version: 1.4.1
    77 * Requires at least: 6.0
    88 * Requires PHP: 8.0
     
    2626 * Plugin constants.
    2727 */
    28 define( 'INTUFIND_VERSION', '1.4.0' );
     28define( 'INTUFIND_VERSION', '1.4.1' );
    2929define( 'INTUFIND_PLUGIN_FILE', __FILE__ );
    3030define( 'INTUFIND_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
  • intufind/trunk/languages/intufind.pot

    r3461186 r3486701  
    695695
    696696#: admin/partials/search-display.php:274
    697 msgid "Searchable Content"
     697msgid "Status"
    698698msgstr ""
    699699
    700700#: admin/partials/search-display.php:276
    701 msgid "Only synced content marked as \"Searchable\" appears in search results. Use the Knowledge page and post list columns to control what's searchable."
     701msgid "Only synced content with an \"Active\" index status appears in search results. Use the Knowledge page and post list columns to control each item's index status."
    702702msgstr ""
    703703
     
    12741274
    12751275#: includes/class-intufind-list-columns.php:167
    1276 msgid "Intufind: Searchable"
     1276msgid "Intufind: Status"
    12771277msgstr ""
    12781278
     
    13201320msgstr ""
    13211321
     1322#: includes/class-intufind-list-columns.php:442
     1323msgid "Set Index: Active"
     1324msgstr ""
     1325
     1326#: includes/class-intufind-list-columns.php:443
     1327msgid "Set Index: Hidden"
     1328msgstr ""
     1329
    13221330#: includes/class-intufind-list-columns.php:444
    1323 msgid "Make Searchable"
    1324 msgstr ""
    1325 
    1326 #: includes/class-intufind-list-columns.php:445
    1327 msgid "Hide from Search"
     1331msgid "Set Index: Disabled"
    13281332msgstr ""
    13291333
     
    13451349
    13461350#. translators: %d: number of posts
    1347 #: includes/class-intufind-list-columns.php:532
    1348 #, php-format
    1349 msgid "%d item made searchable."
    1350 msgid_plural "%d items made searchable."
     1351#: includes/class-intufind-list-columns.php:610
     1352#, php-format
     1353msgid "%d item set to active."
     1354msgid_plural "%d items set to active."
    13511355msgstr[0] ""
    13521356msgstr[1] ""
    13531357
    13541358#. translators: %d: number of posts
    1355 #: includes/class-intufind-list-columns.php:537
    1356 #, php-format
    1357 msgid "%d item hidden from search."
    1358 msgid_plural "%d items hidden from search."
     1359#: includes/class-intufind-list-columns.php:615
     1360#, php-format
     1361msgid "%d item set to hidden."
     1362msgid_plural "%d items set to hidden."
     1363msgstr[0] ""
     1364msgstr[1] ""
     1365
     1366#. translators: %d: number of posts
     1367#: includes/class-intufind-list-columns.php:620
     1368#, php-format
     1369msgid "%d item set to disabled."
     1370msgid_plural "%d items set to disabled."
    13591371msgstr[0] ""
    13601372msgstr[1] ""
     
    13781390msgstr ""
    13791391
    1380 #: includes/class-intufind-list-columns.php:675
    1381 msgid "Now searchable"
    1382 msgstr ""
    1383 
    1384 #: includes/class-intufind-list-columns.php:675
    1385 msgid "Hidden from search"
     1392#: includes/class-intufind-list-columns.php:767
     1393msgid "Index status: Active"
     1394msgstr ""
     1395
     1396#: includes/class-intufind-list-columns.php:768
     1397msgid "Index status: Hidden"
     1398msgstr ""
     1399
     1400#: includes/class-intufind-list-columns.php:769
     1401msgid "Index status: Disabled"
    13861402msgstr ""
    13871403
  • intufind/trunk/readme.txt

    r3484551 r3486701  
    55Tested up to: 6.9
    66Requires PHP: 8.0
    7 Stable tag: 1.4.0
     7Stable tag: 1.4.1
    88WC tested up to: 9.6
    99License: GPLv2 or later
     
    215215== Changelog ==
    216216
     217= 1.4.1 =
     218* Added per-document index status control (active, hidden, disabled) with a new Status column in the Knowledge list
     219* Index status is synced to the cloud on every content update and bulk sync
     220* Hidden catalog visibility products are automatically set to hidden index status
     221
    217222= 1.4.0 =
    218223* Content sync now preserves document structure (headings, lists, bold, links) as markdown for better AI search and chat quality
     
    304309== Upgrade Notice ==
    305310
     311= 1.4.1 =
     312Adds per-document index status controls. You can now set individual posts and products to active, hidden, or disabled directly from the WordPress admin list.
     313
    306314= 1.4.0 =
    307315Content sync now converts HTML to markdown, preserving headings, lists, and formatting for significantly better AI search and chat responses. Re-sync recommended after updating.
Note: See TracChangeset for help on using the changeset viewer.