Last active
August 29, 2015 14:07
-
-
Save birgire/252b72448646d29a64b6 to your computer and use it in GitHub Desktop.
WordPress plugin: Auto Posts Injector For Infinite Scroll
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Plugin Name: Auto Posts Injector For Infinite Scroll | |
* Description: This plugin helps you to automatically inject posts into the main loop. You must use custom code to start the injection. See the example. | |
* Plugin URI: https://gist.github.com/birgire/252b72448646d29a64b6 | |
* Author: birgire | |
* Author URI: https://github.com/birgire | |
* Version: 0.0.1 | |
*/ | |
/* | |
//------------- | |
// Setup: | |
//------------- | |
We don't need to modify any theme code for these injections to work. | |
The plugin uses the "the_post" hook, so we can only inject before a post in the main loop. | |
So just make sure your theme uses this hook (most theme does). | |
You can define the template part used by these injections. | |
You only need to setup the activation/setup code in your functions.php file in your current theme directory | |
(see the usage example file) | |
//------------- | |
// Definitions: | |
//------------- | |
P: Normal posts in the main loop. | |
s: injected (sponsor) posts | |
|: Page break | |
//------------- | |
// Example 1: | |
//------------- | |
posts_per_page = 5 | |
period = 3 | |
items_per_inject = 1 | |
Number of injects before | |
page 1: | |
1 * floor( 5 * 0 / 3 ) + 0 = 0 | |
page 2: | |
1 * floor( 5 * 1 / 3 ) + 0 = 1 | |
page 3: | |
1 * floor( 5 * 2 / 3 ) + 0 = 3 | |
page 4: | |
1 * floor( 5 * 3 / 3 ) - 1 = 4 | |
Page: 1 2 3 4 5 | |
| | | | | | |
Post: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
Inject: | | | | | |
P P P s P P | P s P P P s P | P P s P P P |sP P P s P P | P ... | |
//------------- | |
// Example 2: | |
//------------- | |
posts_per_page = 7 | |
period = 3 | |
items_per_inject = 1 | |
Main Loop: P P P P P P P | P P P P P P P | P P P P P P P | P P P P P P P | ... | |
Injections: P P P s P P P s P | P P s P P P s P P | P s P P P s P P P | s P P P s P P P s P | ... | |
*/ | |
/** | |
* Wrapper to start the injections. | |
*/ | |
function auto_posts_injector( $args, $query_args ) | |
{ | |
if( ! class_exists( 'API_InfiniteScroll' ) ) return; | |
// Setup the injection: | |
$injector = new API_InfiniteScroll( $args ); | |
// Setup the injection query: | |
$injector->query( $query_args ); | |
// Inject: | |
$injector->inject(); | |
} | |
/** | |
* class API_InfiniteScroll | |
* | |
* Inject custom posts into the main loop, through hooks. | |
*/ | |
class API_InfiniteScroll | |
{ | |
protected $injections = null; | |
protected $query = null; | |
protected $pos = array(); | |
protected $args = array(); | |
protected $query_args = array(); | |
protected $inject_mode = false; | |
protected $nr = 1; | |
protected $ppp = 1; | |
protected $page = 1; | |
protected $current_page_injection_nr = 0; | |
public function __construct( $args = array() ) | |
{ | |
$defaults = array( | |
'debug' => false, | |
'repeat' => false, | |
'period' => 1, | |
'items_per_inject' => 1, | |
'template_part' => 'content', | |
); | |
$this->args = wp_parse_args( $args, $defaults ); | |
} | |
public function query( $query_args = array() ) | |
{ | |
$defaults = array( | |
'suppress_filters' => true, | |
); | |
$this->query_args = wp_parse_args( $query_args, $defaults ); | |
} | |
public function inject() | |
{ | |
add_action( 'loop_start', array( $this, 'loop_start' ) ); | |
add_action( 'loop_end', array( $this, 'loop_end' ) ); | |
} | |
protected function debug( $s ) | |
{ | |
if( $this->args['debug'] ) | |
echo $s; | |
} | |
public function injects_before_page( $page ) | |
{ | |
// A correction for injections that can't take place after the last post on the previous page: | |
$delta = 0; | |
if( $page > 1 && 0 === $this->ppp * ( $page - 1 ) % $this->args['period'] ) | |
$delta = - $this->args['items_per_inject'] ; | |
return $this->args['items_per_inject'] * floor( $this->ppp * ( $page - 1 ) / $this->args['period'] ) + $delta; | |
} | |
public function posts_before_page( $page ) | |
{ | |
return $this->ppp * ( $page - 1 ); | |
} | |
public function loop_start( WP_Query $query ) | |
{ | |
$this->query = $query; | |
if( $query->is_main_query() ) | |
{ | |
$this->page = get_query_var( 'paged' ) ? get_query_var( 'paged' ) + 1 : 1 ; | |
$this->ppp = $this->query->get( 'posts_per_page' ); | |
$this->pos = $this->get_injection_positions_by_page( $this->page ); | |
/** | |
* Repeat: Rewind the injections if needed. | |
* | |
* Therefore we need the total number of available injections. | |
*/ | |
if( $this->args['repeat'] ) | |
{ | |
// We can get the total number by running an extra query: | |
$this->query_args['posts_per_page'] = 1; | |
$this->query_args['offset'] = 0; | |
$tmp = new WP_Query( $this->query_args ); | |
$total_injects = 1; | |
if( $tmp->found_posts > 0 ) | |
$total_injects = $tmp->found_posts; | |
// Offset: | |
$this->query_args['offset'] = $this->injects_before_page( $this->page ) % $total_injects ; | |
} | |
else | |
{ | |
// Offset: | |
$this->query_args['offset'] = $this->injects_before_page( $this->page ); | |
} | |
/** | |
* Now we can fetch the injections needed for the current page. | |
*/ | |
// Number of injections to fetch on this current page: | |
$this->query_args['posts_per_page'] = $this->injects_before_page( $this->page + 1 ) - $this->injects_before_page( $this->page ); | |
// Fetch injections: | |
$this->injections = new WP_Query( $this->query_args ); | |
// Activate the injection part through the the_post hook: | |
add_action( 'the_post', array( $this, 'the_post' ) ); | |
} | |
} | |
public function loop_end( WP_Query $query ) | |
{ | |
if( $query->is_main_query() ) | |
remove_action( 'the_post', array( $this, 'the_post' ) ); | |
} | |
public function get_injection_positions_by_page( $page ) | |
{ | |
// Where to inject on the current page: | |
$pos = array(); | |
// When to start the first injection? | |
$start = $this->args['period'] - $this->posts_before_page( $page ) % $this->args['period']; | |
// Last number of post on the current page: | |
$end = $this->ppp; | |
// If the last page ends with a full period, so we still need to inject for that on the current page: | |
if( $page > 1 && 0 === $this->posts_before_page( $page ) % $this->args['period'] ) | |
$pos[] = 1; | |
// Collect the injection positions for the current page: | |
for( $i = 1; $i <= $end; $i++ ) | |
{ | |
if( 0 === ( $i - $start ) % $this->args['period'] ) | |
{ | |
if( $i + 1 <= $end ) | |
$pos[] = $i + 1; // inject after post | |
} | |
} | |
return $pos; | |
} | |
public function the_post() | |
{ | |
// When to inject? | |
if( ! $this->inject_mode // not already in the injection loop | |
&& 0 < $this->nr // post counter | |
&& in_array( $this->nr, $this->pos ) // when to start injection on the current page | |
) | |
{ | |
$this->inject_mode = true; // we are in the injection loop | |
$this->injections->rewind_posts(); // rewind | |
// current injection post: | |
$this->injections->current_post = $this->current_page_injection_nr -1; // starts from: -1 | |
$j = 1; | |
if ( $this->injections->have_posts() ) : | |
$this->debug( '<div style="background: green; padding:50px;">' ); | |
while ( $this->injections->have_posts() ) : | |
$this->injections->the_post(); | |
get_template_part( $this->args['template_part'] ); | |
$this->current_page_injection_nr++; | |
if( $this->args['items_per_inject'] < ++$j ) | |
break; | |
endwhile; | |
wp_reset_postdata(); | |
$this->debug( '</div>' ); | |
endif; | |
$this->inject_mode = false; | |
} | |
if( ! $this->inject_mode ) | |
$this->nr++; | |
} | |
} // end class |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The Automatic Posts Injector For Infinite Scroll - Usage Example | |
* | |
* @version 0.0.1 | |
* @uses content-sponsor.php template part | |
* @uses auto_posts_injector() function | |
* @file functions.php | |
* @see https://gist.github.com/birgire/252b72448646d29a64b6 | |
*/ | |
add_action( 'wp', 'my_injections' ); | |
function my_injections() | |
{ | |
// We want the injection only on the home page: | |
if( ! is_home() ) return; | |
// Activate the injection: | |
if( function_exists( 'auto_posts_injector' ) ) | |
{ | |
auto_posts_injector( | |
array( | |
'debug' => true, // Show the injected posts in green | |
'repeat' => false, // Repeat the injection posts | |
'period' => 3, // Period of injections | |
'items_per_inject' => 1, // How many to inject each time. | |
'template_part' => 'content-sponsor', // Template part for the injection posts | |
), | |
array( | |
'post_type' => 'sponsor' // Any WP_Query here, but notice that "posts_per_page" and "offset" will be overridden. | |
) | |
); | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment