5

So I have a plugin that appends or prepends an enhanced author biography to the content of a page/post/custom post type.

It does this by hooking to either the_content or the_excerpt and appending/prepending content according to the plugin's configuration.

I've started getting support queries where the author biography is appearing in the sidebar of a site via a widget, for example via the Category Posts widget. The widget is using the_excerpt() within a custom query Loop, to pull posts according to a configured category and show the post excerpt within the context of the sidebar.

As a direct effect of this, my plugin's the_excerpt filter hook is being called. What I'd like to do is be able to detect whether my filter hook is being invoked within the context of the sidebar or a widget and conditionally decide whether to append my plugin's content to the post content passed to the filter hook. The pseudo-code would look something like this ...

add_filter ('the_excerpt', array ($this, 'insert_biography_box'));
function insert_biography_box ($content) {
    if (in_sidebar ()) {
        return $content;
    }

    // do code stuff to append/prepend biography content
    return $content;
}

... but after a lot of searching through the WordPress core source, forums and here it doesn't look like a function along the lines of is_sidebar or is_widget (or some other variation on the name) exists.

Is it even possible to determine whether a filter hook function is being called within the context of the sidebar or within a widget?

EDIT: Based on @toscho's suggestion to use is_main_query, I modified my filter hook for the_content and the_excerpt to look like this ...

add_filter ('the_excerpt', array ($this, 'insert_biography_box'));
add_filter ('the_content', array ($this, 'insert_biography_box'));

function insert_biography_box ($content) {
    error_log ('insert_biography_box: current filter=' . current_filter ());
    if (!is_main_query ()) {
        error_log ('Not main query, baling');
        return $content;
    }

    // do code stuff to append/prepend biography content
    $biography = 'some-magic-function-return-value';
    return $content . $biography;
}

Based on this, I was expecting to see the message Not main query, baling in my PHP error log when the Category Posts widget is calling the_excerpt() in the context of the sidebar. But I don't.

For context, the Category Posts widget is querying for posts within the widget's widget method like this (severely paraphrased for clarity) ...

$cat_posts = new WP_Query (...);
while ($cat_posts->have_posts ()) {
    $cat_posts->the_post ();
    the_excerpt ();
}

... am I missing something (very likely) or am I just not getting the context within which I'm using is_main_query() (just as very likely) ... ?

4
  • Maybe you can check if it is a loop, don't know how it will work in your case. codex.wordpress.org/in_the_loop
    – janw
    Oct 10, 2012 at 8:45
  • I did look at this, but the problem is that in 99.9% of cases, the the_excerpt filter hook will be fired by a call to apply_filters from within the context of the the_excerpt() API call and the Codex states that the_excerpt() must be called within The Loop. Ditto for the the_content filter hook and the_content() API call. There is a caveat that someone may be doing some query cleverness which effectively duplicates the Loop environment but that's not the case here. So sadly in_the_loop() will always return true in the main Loop and sidebar Loop(s).
    – Gary Gale
    Oct 10, 2012 at 9:02
  • You can also simply call it outside the loop: apply_filters( 'the_excerpt', get_the_title() ); above the loop would work.
    – kaiser
    Oct 10, 2012 at 11:55
  • @kaiser - that's true but doesn't help here; the problem is that my code doesn't control when the filter hook is fired, so I'm not looking to fire the hook outside of the Loop (you're right that I can manually fire the hook via apply_filters). To clarify, I'm looking to detect within the hook function itself what context the hook is running in, where the context is either "you're in the sidebar" or "you're not in the sidebar".
    – Gary Gale
    Oct 10, 2012 at 12:38

2 Answers 2

7

On a parallel thread over on the WordPress hacks forum, someone suggested using in_the_loop() and that works some of the time, with some plugins that use either the_content and/or the_excerpt, but not all of the time with all the plugins I've been testing against.

Likewise, I've now done further testing using is_main_query() and that works some of the time, with some plugins but not with all of them.

But the magic combination of testing against is_main_query() and in_the_loop() seems to do the trick.

So the (pseudo) code now looks something like this ...

add_filter ('the_excerpt', array ($this, 'insert_biography_box'));
add_filter ('the_content', array ($this, 'insert_biography_box'));

function insert_biography_box ($content) {
    if (!in_the_loop () || !is_main_query ()) {
        return $content;
    }

    // do code stuff to append/prepend biography content
    $biography = 'some-magic-function-return-value';
    return $content . $biography;
}

.. which now gives me precisely the behaviour I wanted, against as many plugins that use the content or excerpt filters in the sidebar and/or footer widgets.

3
  • is_main_query() won't help you here. But otherwise, this code works right? Oct 11, 2012 at 9:50
  • It seems to. I've thrown it against several plugins that use the_content or the_excerpt within a widget, plus a couple of themes that also use these filters to do idebar or footer tricks and so far, it works. Of course, this is far from a complete and authoritative test, but that's a real challenge given the multiplicity of combinations from WordPress core plus plugins plus themes out there on the interwebs.
    – Gary Gale
    Oct 11, 2012 at 11:43
  • Thanks! I have had to use this now on two of the plugins we use. As these plugins are fairly popular, I'm surprised the problem hasn't been caught before. Could it be a problem with the theme instead, that is calling too many triggers when rendering a sidebar? Oct 20, 2015 at 20:37
2

Do not ask for sidebars, ask for the main query:

if ( is_main_query() )
    return;

The reason: the excerpt or the main content can be fetched in other places too, and you really don’t want to create a new condition for each case.

3
  • This definitely seems to be one to file under you learn something new every day. Two points. One; I never even thought to look at the query. Two; I'm going to check this out right now. Will report back. Thank you.
    – Gary Gale
    Oct 10, 2012 at 15:23
  • So this doesn't seem to be working for me. I would expect is_main_query() to return true when called in the context of the main WordPress Loop and return false when called elsewhere, ie: in the sidebar. But it's returning true under all circumstances. See the EDIT to the original question for what I'm seeing.
    – Gary Gale
    Oct 10, 2012 at 16:49
  • 2
    is_main_query does not care which place it's being called from. It checks whether the $wp_query global is running the $wp_the_query global and returns true if they're equal. You can apply the same theory to other conditional tags.
    – kovshenin
    Oct 11, 2012 at 7:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.