Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Extend WP Rig to display related posts using the REST API
<?php
/**
* Template part for displaying related posts.
*
* @package wp_rig
*
* To be placed in template-parts/content/entry-related.php
*/
namespace WP_Rig\WP_Rig;
wp_rig()->print_styles( 'wp-rig-related' );
wp_rig()->display_related_posts();
<?php
/**
* Template part for displaying a post
*
* @package wp_rig
*
* To be placed in template-parts/content/entry.php
*/
namespace WP_Rig\WP_Rig;
?>
<article id="post-<?php the_ID(); ?>" <?php post_class( 'entry' ); ?>>
<?php
get_template_part( 'template-parts/content/entry_header', get_post_type() );
if ( is_search() ) {
get_template_part( 'template-parts/content/entry_summary', get_post_type() );
} else {
get_template_part( 'template-parts/content/entry_content', get_post_type() );
}
get_template_part( 'template-parts/content/entry_footer', get_post_type() );
?>
</article><!-- #post-<?php the_ID(); ?> -->
<?php
if ( is_single() ) {
get_template_part( 'template-parts/content/entry_related' );
}
if ( is_singular( get_post_type() ) ) {
// Show post navigation only when the post type is 'post' or has an archive.
if ( 'post' === get_post_type() || get_post_type_object( get_post_type() )->has_archive ) {
the_post_navigation(
array(
'prev_text' => '<div class="post-navigation-sub"><span>' . esc_html__( 'Previous:', 'wp-rig' ) . '</span></div>%title',
'next_text' => '<div class="post-navigation-sub"><span>' . esc_html__( 'Next:', 'wp-rig' ) . '</span></div>%title',
)
);
}
// Show comments only when the post type supports it and when comments are open or at least one comment exists.
if ( post_type_supports( get_post_type(), 'comments' ) && ( comments_open() || get_comments_number() ) ) {
comments_template();
}
}
<?php
/**
* WP_Rig\WP_Rig\Styles\Component class
*
* @package wp_rig
*
* To be placed in inc/Styles/Component.php
*/
namespace WP_Rig\WP_Rig\Styles;
use WP_Rig\WP_Rig\Component_Interface;
use WP_Rig\WP_Rig\Templating_Component_Interface;
use function WP_Rig\WP_Rig\wp_rig;
use function add_action;
use function add_filter;
use function wp_enqueue_style;
use function wp_register_style;
use function wp_style_add_data;
use function get_theme_file_uri;
use function get_theme_file_path;
use function wp_styles;
use function esc_attr;
use function esc_url;
use function wp_style_is;
use function _doing_it_wrong;
use function wp_print_styles;
use function post_password_required;
use function is_singular;
use function comments_open;
use function get_comments_number;
use function apply_filters;
use function add_query_arg;
/**
* Class for managing stylesheets.
*
* Exposes template tags:
* * `wp_rig()->print_styles()`
*/
class Component implements Component_Interface, Templating_Component_Interface {
/**
* Associative array of CSS files, as $handle => $data pairs.
* $data must be an array with keys 'file' (file path relative to 'assets/css' directory), and optionally 'global'
* (whether the file should immediately be enqueued instead of just being registered) and 'preload_callback'
* (callback function determining whether the file should be preloaded for the current request).
*
* Do not access this property directly, instead use the `get_css_files()` method.
*
* @var array
*/
protected $css_files;
/**
* Associative array of Google Fonts to load, as $font_name => $font_variants pairs.
*
* Do not access this property directly, instead use the `get_google_fonts()` method.
*
* @var array
*/
protected $google_fonts;
/**
* Gets the unique identifier for the theme component.
*
* @return string Component slug.
*/
public function get_slug() : string {
return 'styles';
}
/**
* Adds the action and filter hooks to integrate with WordPress.
*/
public function initialize() {
add_action( 'wp_enqueue_scripts', array( $this, 'action_enqueue_styles' ) );
add_action( 'wp_head', array( $this, 'action_preload_styles' ) );
add_action( 'after_setup_theme', array( $this, 'action_add_editor_styles' ) );
add_filter( 'wp_resource_hints', array( $this, 'filter_resource_hints' ), 10, 2 );
}
/**
* Gets template tags to expose as methods on the Template_Tags class instance, accessible through `wp_rig()`.
*
* @return array Associative array of $method_name => $callback_info pairs. Each $callback_info must either be
* a callable or an array with key 'callable'. This approach is used to reserve the possibility of
* adding support for further arguments in the future.
*/
public function template_tags() : array {
return array(
'print_styles' => array( $this, 'print_styles' ),
);
}
/**
* Registers or enqueues stylesheets.
*
* Stylesheets that are global are enqueued. All other stylesheets are only registered, to be enqueued later.
*/
public function action_enqueue_styles() {
// Enqueue Google Fonts.
$google_fonts_url = $this->get_google_fonts_url();
if ( ! empty( $google_fonts_url ) ) {
wp_enqueue_style( 'wp-rig-fonts', $google_fonts_url, array(), null ); // phpcs:ignore WordPress.WP.EnqueuedResourceParameters.MissingVersion
}
$css_uri = get_theme_file_uri( '/assets/css/' );
$css_dir = get_theme_file_path( '/assets/css/' );
$preloading_styles_enabled = $this->preloading_styles_enabled();
$css_files = $this->get_css_files();
foreach ( $css_files as $handle => $data ) {
$src = $css_uri . $data['file'];
$version = wp_rig()->get_asset_version( $css_dir . $data['file'] );
/*
* Enqueue global stylesheets immediately and register the other ones for later use
* (unless preloading stylesheets is disabled, in which case stylesheets should be immediately
* enqueued based on whether they are necessary for the page content).
*/
if ( $data['global'] || ! $preloading_styles_enabled && is_callable( $data['preload_callback'] ) && call_user_func( $data['preload_callback'] ) ) {
wp_enqueue_style( $handle, $src, array(), $version, $data['media'] );
} else {
wp_register_style( $handle, $src, array(), $version, $data['media'] );
}
wp_style_add_data( $handle, 'precache', true );
}
}
/**
* Preloads in-body stylesheets depending on what templates are being used.
*
* Only stylesheets that have a 'preload_callback' provided will be considered. If that callback evaluates to true
* for the current request, the stylesheet will be preloaded.
*
* Preloading is disabled when AMP is active, as AMP injects the stylesheets inline.
*
* @link https://developer.mozilla.org/en-US/docs/Web/HTML/Preloading_content
*/
public function action_preload_styles() {
// If preloading styles is disabled, return early.
if ( ! $this->preloading_styles_enabled() ) {
return;
}
$wp_styles = wp_styles();
$css_files = $this->get_css_files();
foreach ( $css_files as $handle => $data ) {
// Skip if stylesheet not registered.
if ( ! isset( $wp_styles->registered[ $handle ] ) ) {
continue;
}
// Skip if no preload callback provided.
if ( ! is_callable( $data['preload_callback'] ) ) {
continue;
}
// Skip if preloading is not necessary for this request.
if ( ! call_user_func( $data['preload_callback'] ) ) {
continue;
}
$preload_uri = $wp_styles->registered[ $handle ]->src . '?ver=' . $wp_styles->registered[ $handle ]->ver;
echo '<link rel="preload" id="' . esc_attr( $handle ) . '-preload" href="' . esc_url( $preload_uri ) . '" as="style">';
echo "\n";
}
}
/**
* Enqueues WordPress theme styles for the editor.
*/
public function action_add_editor_styles() {
// Enqueue Google Fonts.
$google_fonts_url = $this->get_google_fonts_url();
if ( ! empty( $google_fonts_url ) ) {
add_editor_style( $this->get_google_fonts_url() );
}
// Enqueue block editor stylesheet.
add_editor_style( 'assets/css/editor/editor-styles.min.css' );
}
/**
* Adds preconnect resource hint for Google Fonts.
*
* @param array $urls URLs to print for resource hints.
* @param string $relation_type The relation type the URLs are printed.
* @return array URLs to print for resource hints.
*/
public function filter_resource_hints( array $urls, string $relation_type ) : array {
if ( 'preconnect' === $relation_type && wp_style_is( 'wp-rig-fonts', 'queue' ) ) {
$urls[] = array(
'href' => 'https://fonts.gstatic.com',
'crossorigin',
);
}
return $urls;
}
/**
* Prints stylesheet link tags directly.
*
* This should be used for stylesheets that aren't global and thus should only be loaded if the HTML markup
* they are responsible for is actually present. Template parts should use this method when the related markup
* requires a specific stylesheet to be loaded. If preloading stylesheets is disabled, this method will not do
* anything.
*
* If the `<link>` tag for a given stylesheet has already been printed, it will be skipped.
*
* @param string ...$handles One or more stylesheet handles.
*/
public function print_styles( string ...$handles ) {
// If preloading styles is disabled (and thus they have already been enqueued), return early.
if ( ! $this->preloading_styles_enabled() ) {
return;
}
$css_files = $this->get_css_files();
$handles = array_filter(
$handles,
function( $handle ) use ( $css_files ) {
$is_valid = isset( $css_files[ $handle ] ) && ! $css_files[ $handle ]['global'];
if ( ! $is_valid ) {
/* translators: %s: stylesheet handle */
_doing_it_wrong( __CLASS__ . '::print_styles()', esc_html( sprintf( __( 'Invalid theme stylesheet handle: %s', 'wp-rig' ), $handle ) ), 'WP Rig 2.0.0' );
}
return $is_valid;
}
);
if ( empty( $handles ) ) {
return;
}
wp_print_styles( $handles );
}
/**
* Determines whether to preload stylesheets and inject their link tags directly within the page content.
*
* Using this technique generally improves performance, however may not be preferred under certain circumstances.
* For example, since AMP will include all style rules directly in the head, it must not be used in that context.
* By default, this method returns true unless the page is being served in AMP. The
* {@see 'wp_rig_preloading_styles_enabled'} filter can be used to tweak the return value.
*
* @return bool True if preloading stylesheets and injecting them is enabled, false otherwise.
*/
protected function preloading_styles_enabled() {
$preloading_styles_enabled = ! wp_rig()->is_amp();
/**
* Filters whether to preload stylesheets and inject their link tags within the page content.
*
* @param bool $preloading_styles_enabled Whether preloading stylesheets and injecting them is enabled.
*/
return apply_filters( 'wp_rig_preloading_styles_enabled', $preloading_styles_enabled );
}
/**
* Gets all CSS files.
*
* @return array Associative array of $handle => $data pairs.
*/
protected function get_css_files() : array {
if ( is_array( $this->css_files ) ) {
return $this->css_files;
}
$css_files = array(
'wp-rig-global' => array(
'file' => 'global.min.css',
'global' => true,
),
'wp-rig-comments' => array(
'file' => 'comments.min.css',
'preload_callback' => function() {
return ! post_password_required() && is_singular() && ( comments_open() || get_comments_number() );
},
),
'wp-rig-content' => array(
'file' => 'content.min.css',
'preload_callback' => '__return_true',
),
'wp-rig-sidebar' => array(
'file' => 'sidebar.min.css',
'preload_callback' => function() {
return wp_rig()->is_primary_sidebar_active();
},
),
'wp-rig-widgets' => array(
'file' => 'widgets.min.css',
'preload_callback' => function() {
return wp_rig()->is_primary_sidebar_active();
},
),
'wp-rig-front-page' => array(
'file' => 'front-page.min.css',
'preload_callback' => function() {
global $template;
return 'front-page.php' === basename( $template );
},
),
'wp-rig-related' => array(
'file' => 'related.min.css',
'preload_callback' => '__return_false',
),
);
/**
* Filters default CSS files.
*
* @param array $css_files Associative array of CSS files, as $handle => $data pairs.
* $data must be an array with keys 'file' (file path relative to 'assets/css'
* directory), and optionally 'global' (whether the file should immediately be
* enqueued instead of just being registered) and 'preload_callback' (callback)
* function determining whether the file should be preloaded for the current request).
*/
$css_files = apply_filters( 'wp_rig_css_files', $css_files );
$this->css_files = array();
foreach ( $css_files as $handle => $data ) {
if ( is_string( $data ) ) {
$data = array( 'file' => $data );
}
if ( empty( $data['file'] ) ) {
continue;
}
$this->css_files[ $handle ] = array_merge(
array(
'global' => false,
'preload_callback' => null,
'media' => 'all',
),
$data
);
}
return $this->css_files;
}
/**
* Returns Google Fonts used in theme.
*
* @return array Associative array of $font_name => $font_variants pairs.
*/
protected function get_google_fonts() : array {
if ( is_array( $this->google_fonts ) ) {
return $this->google_fonts;
}
$google_fonts = array(
'Roboto Condensed' => array( '400', '400i', '700', '700i' ),
'Crimson Text' => array( '400', '400i', '600', '600i' ),
);
/**
* Filters default Google Fonts.
*
* @param array $google_fonts Associative array of $font_name => $font_variants pairs.
*/
$this->google_fonts = (array) apply_filters( 'wp_rig_google_fonts', $google_fonts );
return $this->google_fonts;
}
/**
* Returns the Google Fonts URL to use for enqueuing Google Fonts CSS.
*
* Uses `latin` subset by default. To use other subsets, add a `subset` key to $query_args and the desired value.
*
* @return string Google Fonts URL, or empty string if no Google Fonts should be used.
*/
protected function get_google_fonts_url() : string {
$google_fonts = $this->get_google_fonts();
if ( empty( $google_fonts ) ) {
return '';
}
$font_families = array();
foreach ( $google_fonts as $font_name => $font_variants ) {
if ( ! empty( $font_variants ) ) {
if ( ! is_array( $font_variants ) ) {
$font_variants = explode( ',', str_replace( ' ', '', $font_variants ) );
}
$font_families[] = $font_name . ':' . implode( ',', $font_variants );
continue;
}
$font_families[] = $font_name;
}
$query_args = array(
'family' => implode( '|', $font_families ),
);
return add_query_arg( $query_args, 'https://fonts.googleapis.com/css' );
}
}
<?php
/**
* WP_Rig\WP_Rig\Theme class
*
* @package wp_rig
*/
namespace WP_Rig\WP_Rig;
use InvalidArgumentException;
/**
* Main class for the theme.
*
* This class takes care of initializing theme features and available template tags.
*/
class Theme {
/**
* Associative array of theme components, keyed by their slug.
*
* @var array
*/
protected $components = [];
/**
* The template tags instance, providing access to all available template tags.
*
* @var Template_Tags
*/
protected $template_tags;
/**
* Constructor.
*
* Sets the theme components.
*
* @param array $components Optional. List of theme components. Only intended for custom initialization, typically
* the theme components are declared by the theme itself. Each theme component must
* implement the Component_Interface interface.
*
* @throws InvalidArgumentException Thrown if one of the $components does not implement Component_Interface.
*/
public function __construct( array $components = [] ) {
if ( empty( $components ) ) {
$components = $this->get_default_components();
}
// Set the components.
foreach ( $components as $component ) {
// Bail if a component is invalid.
if ( ! $component instanceof Component_Interface ) {
throw new InvalidArgumentException(
sprintf(
/* translators: 1: classname/type of the variable, 2: interface name */
__( 'The theme component %1$s does not implement the %2$s interface.', 'wp-rig' ),
gettype( $component ),
Component_Interface::class
)
);
}
$this->components[ $component->get_slug() ] = $component;
}
// Instantiate the template tags instance for all theme templating components.
$this->template_tags = new Template_Tags(
array_filter(
$this->components,
function( Component_Interface $component ) {
return $component instanceof Templating_Component_Interface;
}
)
);
}
/**
* Adds the action and filter hooks to integrate with WordPress.
*
* This method must only be called once in the request lifecycle.
*/
public function initialize() {
array_walk(
$this->components,
function( Component_Interface $component ) {
$component->initialize();
}
);
}
/**
* Retrieves the template tags instance, the entry point exposing template tag methods.
*
* Calling `wp_rig()` is a short-hand for calling this method on the main theme instance. The instance then allows
* for actual template tag methods to be called. For example, if there is a template tag called `posted_on`, it can
* be accessed via `wp_rig()->posted_on()`.
*
* @return Template_Tags Template tags instance.
*/
public function template_tags() : Template_Tags {
return $this->template_tags;
}
/**
* Retrieves the component for a given slug.
*
* This should typically not be used from outside of the theme classes infrastructure.
*
* @param string $slug Slug identifying the component.
* @return Component_Interface Component for the slug.
*
* @throws InvalidArgumentException Thrown when no theme component with the given slug exists.
*/
public function component( string $slug ) : Component_Interface {
if ( ! isset( $this->components[ $slug ] ) ) {
throw new InvalidArgumentException(
sprintf(
/* translators: %s: slug */
__( 'No theme component with the slug %s exists.', 'wp-rig' ),
$slug
)
);
}
return $this->components[ $slug ];
}
/**
* Gets the default theme components.
*
* This method is called if no components are passed to the constructor, which is the common scenario.
*
* @return array List of theme components to use by default.
*/
protected function get_default_components() : array {
$components = [
new Localization\Component(),
new Base_Support\Component(),
new Editor\Component(),
new Accessibility\Component(),
new Image_Sizes\Component(),
new Lazyload\Component(),
new AMP\Component(),
new PWA\Component(),
new Comments\Component(),
new Nav_Menus\Component(),
new Sidebars\Component(),
new Custom_Background\Component(),
new Custom_Header\Component(),
new Custom_Logo\Component(),
new Post_Thumbnails\Component(),
new Customizer\Component(),
new Styles\Component(),
new Related_Posts\Component(),
];
if ( defined( 'JETPACK__VERSION' ) ) {
$components[] = new Jetpack\Component();
}
return $components;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.