Last active
December 11, 2017 21:49
-
-
Save mikeschinkel/f70c9298a9e71d029c6f to your computer and use it in GitHub Desktop.
Proof of concept showing how to implement virtual posts in WordPress but requires `make_post_instance` hook (or similar) to exist. See: https://core.trac.wordpress.org/ticket/12955#comment:24
This file contains 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: Virtual Posts | |
* | |
* @see https://github.com/usagov/Federal-Agency-Directory-API-Documentation | |
*/ | |
class Virtual_Posts { | |
const POST_TYPE = 'vp_fed_mob_apps'; | |
const TRANSIENT_KEY = 'vp_federal_mobile_app_ids'; | |
const TIMESTAMP_KEY = 'vp_timestamp'; | |
const DATE_FORMAT = 'Y-m-d h:i:s'; | |
const API_URL = 'http://apps.usa.gov/apps-gallery/api/registrations.json'; | |
/** | |
* @var array List of App IDs from the API. | |
*/ | |
private static $_app_ids; | |
/** | |
* | |
*/ | |
static function on_load() { | |
add_action( 'init', array( __CLASS__, '_init' ) ); | |
add_action( 'pre_get_posts', array( __CLASS__, '_pre_get_posts' ) ); | |
add_filter( 'make_post_instance', array( __CLASS__, '_make_post_instance' ) ); | |
add_filter( 'wp_count_posts', array( __CLASS__, '_wp_count_posts' ) ); | |
} | |
/** | |
* Refresh the post from the API if stale by 15 minutes or longer | |
* | |
* @param object $post | |
* | |
* @return array|null|WP_Post | |
*/ | |
static function _make_post_instance( $post ) { | |
static $posts_processed = array(); | |
if ( ! isset( $posts_processed[ $post->ID ] ) ) { | |
$posts_processed[ $post->ID ]= $post->ID; | |
$timestamp = get_post_meta( $post->ID, self::TIMESTAMP_KEY, true ); | |
if ( ! $timestamp || $timestamp < date( self::DATE_FORMAT, strtotime( '-15 minutes' ) ) ) { | |
if ( 1 <= count( $apps = self::_call_api( "Id={$post->post_name}" ) ) ) { | |
self::_update_app( $post->ID, $apps[0] ); | |
} | |
$post = get_post( $post->ID ); | |
} | |
} | |
return $post; | |
} | |
/** | |
* @param $counts | |
* | |
* @return mixed | |
*/ | |
static function _wp_count_posts( $counts ) { | |
$app_ids = self::_app_ids(); | |
$counts->publish = is_array( $app_ids ) ? count( $app_ids ) : 0; | |
return $counts; | |
} | |
/** | |
* @param WP_Query $query | |
*/ | |
static function _pre_get_posts( $query ) { | |
if ( $query->is_main_query() && self::POST_TYPE == $query->get( 'post_type' ) ) { | |
$post_ids = self::_post_ids( $query ); | |
$needed = array_filter( array_keys( $post_ids ), function ( $post_id ) use ( $post_ids ) { | |
return 0 === $post_ids[ $post_id ]; | |
}); | |
if ( count( $needed ) ) { | |
$results = self::_call_api( 'Id=' . implode( '&Id=', $needed ) ); | |
foreach( $results as $result ) { | |
self::_maybe_add_app( $result ); | |
} | |
} | |
$query->set( 'orderby', 'ID' ); | |
$query->set( 'order', 'ASC' ); | |
} | |
} | |
/** | |
* @param int $post_id | |
* @return int[] | |
*/ | |
private static function _update_app( $post_id, $app ) { | |
if ( ! is_numeric( $post_id ) ) { | |
$post = get_page_by_path( $post_id, OBJECT, self::POST_TYPE ); | |
$post_id = $post ? $post->ID : false; | |
} | |
if ( $post_id ) { | |
wp_update_post( array( | |
'ID' => $post_id, | |
'post_name' => $app->Id, | |
'post_type' => self::POST_TYPE, | |
'post_title' => $app->Name, | |
'post_status' => 'publish', | |
'post_content' => $app->Long_Description, | |
'post_excerpt' => $app->Short_Description, | |
'post_date_gmt' => date( self::DATE_FORMAT, $app->created ), | |
'post_modified_gmt' => date( self::DATE_FORMAT, $app->updated ), | |
) ); | |
$fields = array( | |
'Id', | |
'Name', | |
'Long_Description', | |
'Short_Description', | |
'created', | |
'updated' | |
); | |
foreach ( $fields as $field_name ) { | |
unset( $app->$field_name ); | |
} | |
foreach ( (array) $app as $field_name => $value ) { | |
$value = is_array( $value ) ? print_r( $value, true ) : $value; | |
update_post_meta( $post_id, $field_name, $value ); | |
} | |
update_post_meta( $post_id, self::TIMESTAMP_KEY, date( self::DATE_FORMAT, time() ) ); | |
} | |
} | |
/** | |
* @param object $app | |
*/ | |
private static function _maybe_add_app($app) { | |
$app_post = get_page_by_path( $app->Id, OBJECT, self::POST_TYPE ); | |
if ( ! $app_post ) { | |
$post_id = wp_insert_post( array( | |
'post_name' => $app->Id, | |
'post_type' => self::POST_TYPE, | |
'post_title' => $app->Name, | |
)); | |
if ( $post_id ) { | |
self::_update_app( $post_id, $app ); | |
} | |
} | |
} | |
/** | |
* @param WP_Query $query | |
* @return int[] | |
*/ | |
private static function _post_ids( $query ) { | |
global $wpdb; | |
$app_ids = self::_app_ids(); | |
$record_count = $query->get( 'posts_per_page' ); | |
$paged = $query->get('paged'); | |
$offset = $paged ? ( $paged - 1 ) * $record_count + 1 : 1; | |
$app_ids = array_slice( $app_ids, $offset, $record_count ? $record_count : 20 ); | |
$app_ids_sql = implode( "','", array_map( 'sanitize_key', $app_ids ) ); | |
$post_ids = array(); | |
$sql = "SELECT ID,post_name FROM {$wpdb->posts} WHERE post_name IN ('{$app_ids_sql}');"; | |
if ( is_array( $results = $wpdb->get_results( $sql ) ) ) { | |
foreach( $results as $result ) { | |
$post_ids[ $result->post_name ] = $result->ID; | |
} | |
} | |
/** | |
* Now add in the missing ones | |
*/ | |
foreach( $app_ids as $app_id ) { | |
if ( ! isset( $post_ids[ $app_id ] ) ) { | |
$post_ids[ $app_id ] = 0; | |
} | |
} | |
return $post_ids; | |
} | |
/** | |
* Load just the IDs into a transient | |
*/ | |
static function _init() { | |
register_post_type( self::POST_TYPE, array( | |
'public' => true, | |
'label' => __( 'Federal Mobile Apps', 'virtpost' ), | |
'supports' => array( | |
'title', | |
'editor', | |
'custom-fields', | |
), | |
'rewrite' => array( | |
'with_front' => false, | |
'slug' => 'agencies' | |
), | |
)); | |
} | |
/** | |
* Load just the IDs into a transient | |
*/ | |
private static function _app_ids() { | |
if ( isset( self::$_app_ids ) ) { | |
return self::$_app_ids; | |
} | |
self::$_app_ids = get_transient( self::TRANSIENT_KEY ); | |
if ( ! self::$_app_ids ) { | |
if ( $results = self::_call_api( 'select=Id' ) ) { | |
$app_ids = array(); | |
foreach( $results as $index => $app ) { | |
$app_ids[] = $app->Id; | |
} | |
set_transient( self::TRANSIENT_KEY, $app_ids, 15*60 ); // Cached for 15 minutes | |
self::$_app_ids = $app_ids; | |
} | |
} | |
return self::$_app_ids; | |
} | |
/** | |
* @param $query_vars | |
* | |
* @return object[]|null | |
*/ | |
static function _call_api( $query_vars ) { | |
$response = wp_remote_get( self::API_URL . "?{$query_vars}" ); | |
if ( 200 == wp_remote_retrieve_response_code( $response ) ) { | |
$json = json_decode( wp_remote_retrieve_body( $response ) ); | |
if ( is_array( $json->results ) ) { | |
$results = array(); | |
foreach( $json->results as $index => $object ) { | |
$results[ $index ] = $object; | |
} | |
return $results; | |
} | |
} | |
return null; | |
} | |
} | |
Virtual_Posts::on_load(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment