Opened 8 months ago

Last modified 8 months ago

#5746 new defect

wporg-glossary plugin doesn't correctly handle being run twice over the same content

Reported by: pbiron Owned by:
Milestone: Priority: normal
Component: Make (Get Involved) / P2 Keywords:


In Glossary_Handler::glossary_links(), there is logic that tries to skip adding links in content...but that logic is incorrect.

In particular,

// Skip the Glossary item container span, for when the_content is run over the_content.
if ( $matches && 'span' == $matches[0] && false !== strpos( $element, "class='glossary-item-container'" ) ) {
        if ( ! $is_end_tag ) {
                array_unshift( $inside_block, $matches[0] );
        } elseif ( $inside_block && $matches[0] === $inside_block[0] ) {
                array_shift( $inside_block );


Unfortunately, the false !== strpos( $element, "class='glossary-item-container'" ) clause in the conditional will only ever be true on the opening tag. Because of that, the span that gets pushed onto $inside_block will never get popped.

The result is that once it sees an opening span with that class, $inside_block will never be empty again (unless there is some seriously unbalanced markup involving any of the $ignore_elements) and from that point on no other occurrences of glossary terms will get the hovercard markup added to them.

I discovered this while investigating possibly using this plugin on a client site. It's unclear how likely this issue is to surface on .org content.

Here's a simple example to illustrate the bug (abstracted from that client site). Suppose the glossary contains term1 and term2, and a post with the following content:

[some_shortcode]this is term1[/some_shortcode]
<p>a paragraph with term2</p>

Further suppose the plugin that registers [some_shortcode], does something like:

add_shortcode( 'some_shortcode', function( $atts, $content ) {
        return apply_filters( 'the_content', $content );
} );

So, what Glossary_Handler::glossary_links() sees the 2nd time is:

<span tabindex="0" class="glossary-item-container">some term<span class="glossary-item-hidden-content"><span class="glossary-item-header">Administrator</span> <span class="glossary-item-description">some definition</span></span></span></span>
<p>a paragraph with term2</p>

The result is that term2 does not end up with a hovercard.

I'm not sure what the easiest/best way to fix this is. Conceptually, what needs to happen is once a <span class='glossary-item-container'> is found, it needs to start pushing/popping tags onto another stack so that it can detect when it's seen the associated closing </span>, in which case it should pop the span off of $inside_block. But that can get messy real quick, especially if it needs to account for unbalanced tags in the glossary item description.

Change History (3)

#1 @dd32
8 months ago

~ The meeting calendar is developed here: ~

~ @pbiron Would you be able to create this ticket upstream there? ~

Last edited 8 months ago by dd32 (previous) (diff)

#2 follow-up: @pbiron
8 months ago

Is there some relation to the meeting calendar that I'm unaware of? Like, does it define a shortcode (or block?) t hat could surface this problem?

#3 in reply to: ↑ 2 @dd32
8 months ago

Replying to pbiron:

Is there some relation to the meeting calendar that I'm unaware of? Like, does it define a shortcode (or block?) t hat could surface this problem?

Well, I blame my lack of coffee. Apologies @pbiron! I was on auto-pilot after reponding to a different ticket.

The code is hosted in plugins:
This is the appropriate place for the issue though.

Note: See TracTickets for help on using tickets.