DEV Community

Webequipe
Webequipe

Posted on

Extending PDF Search with Custom Hooks: Building Your Own Post-Index Logic

 The plugin does the indexing. The hooks are what let you do something with it. Here's how I used them to build a real feature I didn't want to hardcode into core.

A plugin that only does what its settings page allows is limiting for anyone who wants to build on top of it. So alongside the core indexing pipeline, I exposed a small set of hooks that fire at the points someone's most likely to want to plug into: before a PDF is indexed, after it's indexed, when indexing fails, and when search results are about to be returned.

This post walks through what those hooks actually look like, and a real example of using them: auto-tagging PDFs based on their extracted content the moment indexing finishes.

The hooks themselves

do_action( 'webequipe_pdf_search_before_index', $attachment_id );

// indexing happens here

do_action( 'webequipe_pdf_search_after_index', $attachment_id, $extracted_text );

// on failure instead:
do_action( 'webequipe_pdf_search_index_failed', $attachment_id, $error_message );

// and on the search side:
$results = apply_filters( 'webequipe_pdf_search_results', $results, $search_term );
Enter fullscreen mode Exit fullscreen mode

Nothing exotic, standard WordPress action and filter pattern. The part that matters is what gets passed to each one. The after_index hook hands you the attachment ID and the full extracted text, which is enough to actually do something meaningful without re-parsing the PDF yourself.

Example: auto-tagging PDFs by content

Say you want PDFs automatically tagged based on what's actually inside them, no manual tagging required. Hooking after_index gets you there:

add_action( 'webequipe_pdf_search_after_index', 'auto_tag_pdf_by_content', 10, 2 );

function auto_tag_pdf_by_content( $attachment_id, $extracted_text ) {
    $keyword_map = array(
        'invoice'      => 'finance',
        'employment'   => 'hr',
        'lease'        => 'legal',
        'safety data'  => 'compliance',
    );

    $tags_to_apply = array();

    foreach ( $keyword_map as $keyword => $tag ) {
        if ( stripos( $extracted_text, $keyword ) !== false ) {
            $tags_to_apply[] = $tag;
        }
    }

    if ( ! empty( $tags_to_apply ) ) {
        wp_set_object_terms( $attachment_id, $tags_to_apply, 'pdf_category', true );
    }
}
Enter fullscreen mode Exit fullscreen mode

Every time a PDF finishes indexing, this checks the extracted text for a handful of keywords and applies matching taxonomy terms automatically. Upload 200 old invoices and HR documents, and they sort themselves without anyone touching a tagging screen.

This is a simple keyword match on purpose, it's meant to be a starting point, not a complete classification system. Swap the keyword map for something smarter, a proper taxonomy classifier, or an external API call, and the hook still does its job the same way.

Example: getting notified when indexing fails

The failure hook is useful for anyone managing a large library who doesn't want to manually check an activity log every day:

add_action( 'webequipe_pdf_search_index_failed', 'notify_on_index_failure', 10, 2 );

function notify_on_index_failure( $attachment_id, $error_message ) {
    $file_name = basename( get_attached_file( $attachment_id ) );

    wp_mail(
        get_option( 'admin_email' ),
        'PDF indexing failed: ' . $file_name,
        "Attachment ID {$attachment_id} failed to index. Error: {$error_message}"
    );
}
Enter fullscreen mode Exit fullscreen mode

Swap wp_mail for a Slack webhook call and you've got a real-time alert instead of an email, same hook, different destination.

Filtering results before they're shown

The results filter runs right before results go to the template, which makes it the right place to reorder or annotate results without touching core search logic at all:

add_filter( 'webequipe_pdf_search_results', 'boost_recent_pdfs', 10, 2 );

function boost_recent_pdfs( $results, $search_term ) {
    usort( $results, function( $a, $b ) {
        $a_date = get_the_date( 'U', $a->attachment_id );
        $b_date = get_the_date( 'U', $b->attachment_id );

        if ( $a->relevance === $b->relevance ) {
            return $b_date <=> $a_date;
        }

        return $b->relevance <=> $a->relevance;
    });

    return $results;
}
Enter fullscreen mode Exit fullscreen mode

This keeps relevance as the primary sort but breaks ties in favor of newer documents, useful on a site where an outdated policy PDF and its replacement can otherwise tie on relevance.

Why hooks instead of more settings

I could have tried to anticipate every use case and turn it into a checkbox in the settings page. That doesn't scale, and it usually ends up as a settings page nobody fully understands. Exposing a handful of well-placed hooks instead means the core plugin stays focused on indexing and search, and anyone with a specific need, auto-tagging, alerting, custom ranking, can build it without waiting for a plugin update or forking anything.

What's the last plugin you extended with hooks instead of asking the author for a new setting? Curious what people have actually built this way.

These are the actual hooks available in WebEquipe PDF Search if you want to try any of this on a real install.

Top comments (0)