Skip to content

Instantly share code, notes, and snippets.

@janboddez
Last active February 18, 2022 09:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save janboddez/31a44dbf7fa952122c663b5e0606f059 to your computer and use it in GitHub Desktop.
Save janboddez/31a44dbf7fa952122c663b5e0606f059 to your computer and use it in GitHub Desktop.
<?php
/**
* Adds a "copy" of your site's "blog" page at `/stream/`. Visit "Settings >
* Permalinks" to flush rewrite rules after adding this code.
*/
add_action( 'init', function() {
add_rewrite_rule( '^stream/feed/?', 'index.php?feed=rss2', 'top' );
add_rewrite_rule( '^stream/page/([0-9]{1,})/?', 'index.php?paged=$matches[1]', 'top' );
add_rewrite_rule( '^stream/?', 'index.php', 'top' );
} );
/**
* Excludes a certain category from (only) the (actual) "blog" page.
*/
add_action( 'pre_get_posts', function( $query ) {
global $wp;
if ( $query->is_main_query()
&& 0 !== strpos( $wp->request, 'stream' )
&& ( is_home() || ( is_feed() && ! is_comment_feed() && ( empty( $query->query_vars['post_type'] ) || 'post' === $query->query_vars['post_type'] ) ) ) ) {
// Main query, not a `stream` URL, and either the "blog" page or a feed (but not a comment feed!) for which the post type is either `post` or not set at all.
$query->set( 'category__not_in', array( 1 ) );
}
} );
@metbril
Copy link

metbril commented Feb 17, 2022

Wouldn't that pre_get_posts not require a check for is_admin() pages? e.g.

if ( $query->is_main_query() && ! is_admin() && is_home() && 0 !== strpos( $request, 'stream' ) ) {

https://developer.wordpress.org/reference/hooks/pre_get_posts/#exclude-pages-from-search-results

@janboddez
Copy link
Author

I don't think is_home() and is_admin() can ever be true at the same time.

@janboddez
Copy link
Author

janboddez commented Feb 17, 2022

Added a "stream" feed, which is in fact the "default," main feed. And included the main feed in the pre_get_posts "filter". (The thing is, it's quite hard to determine whether a feed is in fact the main "post" feed. E.g., if you---elsewhere---were to include a CPT in this main feed, the if statement as it is now written would no longer be true.)

One could rewrite that last bit, split the conditions or use early returns, etc. Gave it a try but it just became so long. This approach, while "ugly," is actually fairly readable.

@janboddez
Copy link
Author

Note that this still won't create an Atom feeds, etc. Everything needs to be explicitly defined. (But using a page template and WP_Query would also not result in "automagic" feeds. The only way to get feeds, etc., "for free," is using something like CPTs or custom taxonomies.)

@metbril
Copy link

metbril commented Feb 18, 2022

As far as I've been able to see, the pre_get_posts gives me all the feeds out of the box with the filtered posts.

I've added some extra rewrites for all (known to me) possible feeds:

/**
 * Adds a "copy" of your site's "blog" page at `/stream/`. Visit "Settings >
 * Permalinks" to flush rewrite rules after adding this code.
 */
add_action( 'init', function() {
	/* feeds by plugins */
	add_rewrite_rule( '^stream/feed/mf2/?', 'index.php?feed=mf2', 'top' );
	add_rewrite_rule( '^stream/feed/json/?', 'index.php?feed=json', 'top' );
	/* standard feeds */
	add_rewrite_rule( '^stream/feed/atom/?', 'index.php?feed=atom', 'top' );
	add_rewrite_rule( '^stream/feed/rdf/?', 'index.php?feed=rdf', 'top' );
	add_rewrite_rule( '^stream/feed/rss2/?', 'index.php?feed=rss2', 'top' );
	add_rewrite_rule( '^stream/feed/rss/?', 'index.php?feed=rss2', 'top' );
	add_rewrite_rule( '^stream/feed/?', 'index.php?feed=rss2', 'top' );

	add_rewrite_rule( '^stream/page/([0-9]{1,})/?', 'index.php?paged=$matches[1]', 'top' );
	add_rewrite_rule( '^stream/?', 'index.php', 'top' );
} );

@metbril
Copy link

metbril commented Feb 18, 2022

(The thing is, it's quite hard to determine whether a feed is in fact the main "post" feed. E.g., if you---elsewhere---were to include a CPT in this main feed, the if statement as it is now written would no longer be true.)

To work around this, couldn't you just exclude pages only? So replace

'post' === $query->query_vars['post_type']

with

'page' !== $query->query_vars['post_type']

@metbril
Copy link

metbril commented Feb 18, 2022

The story continues...

The stream page broadcasts only the (now limited) root feed and not the stream feed. For a transparent solution this should be included in the stream <head> section like so:

<link rel="alternate" type="application/rss+xml" title="Example.com Stream" href="https://example.com/stream/feed/">

LOL: this also applies for any other "free" feed, like the ATOM, JSON or MF2 feeds.

I'm slowly getting the idea that this will become a never ending story.

@janboddez
Copy link
Author

janboddez commented Feb 18, 2022

I've added some extra rewrites for all (known to me) possible feeds.

Smart! On one of my blogs, I do it the other way around. All "standard" feeds except RSS2 (for which I use a custom template, that's why) lead to a 404 error. Then I only have to worry about that one anymore. :-)

As far as I've been able to see, the pre_get_posts gives me all the feeds out of the box with the filtered posts.

Hmm, not sure if I understand it correctly, but this bit should actually prevent that:

0 !== strpos( $wp->request, 'stream' )

You might wanna think of an alternative, then. 0 !== strpos( $_SERVER['REQUEST_URI'], '/stream' ) or something.

To work around this, couldn't you just exclude pages only?

Hm, that would also target, say, category or CPT feeds, and so on. You really want to target only the main feed, and that takes some knowledge on what is really included there. Like, if in another must-use plugin you include a CPT or something, you may want to explicitly check for array( 'post', 'my_cpt_slug' ) === $query->query_vars['post_type'] instead. Only way, I think, to guarantee you're targeting the main feed only.

Come to think of it, my version probably also targets category feeds. Better to explicitly check the URL, using, e.g., 'feed' === $wp->request. (Untested!)

The best way to go about this is probably "debug log" the $wp and $query objects and see what parameters are actually present for the feeds you want to target.

The stream page broadcasts only the (now limited) root feed and not the stream feed.

This is normal. WordPress doesn't know about the feed. Same reason it wasn't "auto-generated." Using rewrite rules like this is much more low-level than, e.g., custom post types, which give you a ton of functionality "for free." All we're doing is defining new "routes" and linking them to a "controller method." Nothing else ("magically") happens.

Look into the wp_head action, which will allow you to add markup to your site's <head>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment