WordPress.org

Make WordPress Core

Opened 19 months ago

Last modified 2 months ago

#21894 new defect (bug)

<!--more--> tag does nothing in secondary loops when is_single because of $more global

Reported by: jeremyclarke Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Posts, Post Types Keywords: has-patch
Focuses: template Cc:

Description

In the sidebar of single posts I have a listing of short posts from a particular category. We recently started using the <!--more--> system to define teasers because in this case excerpts are overkill. The "more" system works for archive views but does nothing in the sidebar of single posts. The reason is the overly global nature of the global $more variable. It gets defined in setup_postdata() like this:

if ( is_single() || is_page() || is_feed() )
	$more = 1;

Then in get_the_content() global $more is checked to determine whether to apply the effect of the <!--more--> tag or not:

if ( $more ) {
	// show full content with span ID 
} else {
	// show intro text and more link
}

This means that any time is_single() is true all posts shown during the pageload ignore <!--more-->, even those who's is_single view we are not on.

This seems like an understandable result of the unpopularity of the more tag (and rareness of showing "full" posts in secondary loops), but also something that should be fixed. Only the post who's single view we are looking at should have the more tag disabled based on the is_single() check.

SOLUTION

As far as I can tell what's needed is to modify get_the_content() and in addition to using global $more, also check if the post being shown is the same as get_queried_object(). My first instinct was to just add the check in the if statement above:

if ( $more AND ( $post-ID == get_the_queried_object_id() ) ) {
	// show full content with span ID 
} else {
	// show intro text and more link
}

Unfortunately that will screw with the is_feed()-related checks for global $more. Overall my investigation of $more is making me think it should be removed entirely, as it's only used a few times and in all cases its meaning is vague.

Either way I think the simplest way to handle the issue in get_the_content() without completely stripping out $more is to use $more as the base for a $show_full_content variable, then set that to false if $post->ID doesn't match get_queried_object_id():

$show_full_content = $more;
if ( is_single() OR is_page() ) {
	if ($post->ID != get_queried_object_id())
		$show_full_content = false;
}

The new variable gives a better sense of what it will do inside this function, and lets us change the value without stomping the global $more. The attached patch fixes get_the_content() as described above.

If core devs are willing to I think the best solution would be to remove all uses of $more and simply replace them with the appropriate logic for the given situation like I've done above for get_the_content().

Attachments (2)

fix-more-tag-599068.diff (1.1 KB) - added by jeremyclarke 19 months ago.
fix more tag in get_the_content() in wp-includes/post-template.php
fix_more_setup_postdata_21852.diff (480 bytes) - added by jeremyclarke 19 months ago.
fix more tag via setup_postdata

Download all attachments as: .zip

Change History (10)

jeremyclarke19 months ago

fix more tag in get_the_content() in wp-includes/post-template.php

comment:1 jeremyclarke19 months ago

While preparing a plugin-based temporary solution to this I realized that I attacked it from the wrong angle. I somehow didn't think of the fact that setup_postdata() gets run for each post, and $more is initialized for each spin of the loop.

It seems clear that the actual right place to put the new logic is in setup_postdata():

if ( ( is_single() || is_page() ) && ( $id == get_queried_object_id() ) )
	$more = 1;
if ( is_feed())	
	$more = 1;

Sorry for adding noise. I think changing setup_postdata() si an even more obvious win than the change in get_the_content(), though both will solve my problem.

jeremyclarke19 months ago

fix more tag via setup_postdata

comment:2 jeremyclarke19 months ago

That last patch is slightly different than described. I realized I can just pass $id to is_single() and is_page() to ensure it checks that we're dealing with the core single-post for the page. It also sets $more to false before the check, otherwise it gets set to true on the first loop (the main single post) and never gets set to false for other posts that don't match the if statement.

comment:3 SergeyBiryukov19 months ago

  • Component changed from Formatting to Template
  • Version trunk deleted

comment:4 knutsp19 months ago

  • Cc knut@… added

comment:5 knutsp17 months ago

  • Keywords has-patch added

Can we fix this for 3.6, please?

comment:6 Veraxus14 months ago

I agree, this should be fixed, but I'm not sure the provided patch is the right answer.

Currently, the workaround solution for developers is to explicitly set $more = false before the new custom loop, which isn't a huge deal... (although if you want to preserve the original value, you would have to briefly store it in another variable and restore it after the loop).

I think a more developer-centric solution would be to provide the ability to set handing of the more tag as a query parameter... although that would involve modifying more WordPress components to provide support.

comment:7 thpani10 months ago

  • Cc thpani added

comment:8 nacin2 months ago

  • Component changed from Template to Posts, Post Types
  • Focuses template added
Note: See TracTickets for help on using tickets.