<?php /** * Plugin functionality. * * @package ETH_Redirect_To_Latest_Post */ /** * Class ETH_Redirect_To_Latest_Post. */ class ETH_Redirect_To_Latest_Post { /** * PLUGIN SETUP */ /** * Singleton. * * @var self */ private static $instance; /** * Instantiate singleton. */ public static function get_instance() { if ( ! is_a( self::$instance, __CLASS__ ) ) { self::$instance = new self(); } return self::$instance; } /** * Dummy magic method. */ public function __clone() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ uh?', 'eth_redirect_to_latest_post' ), '0.1' ); } /** * Dummy magic method. */ public function __wakeup() { _doing_it_wrong( __FUNCTION__, esc_html__( 'Cheatin’ uh?', 'eth_redirect_to_latest_post' ), '0.1' ); } /** * Dummy magic method. * * @param string $name Method name. * @param array $args Method arguments. * @return null */ public function __call( $name = '', $args = array() ) { return null; } /** * Plugin's option name. * * @var string */ private $plugin_option_name = 'eth-redirect-to-latest'; /** * Plugin's slug. * * @var string */ private $slug = ''; /** * Plugin's fallback slug. * * @var string */ private $default_slug = ''; /** * Register plugin's setup action. */ private function __construct() { add_action( 'init', array( $this, 'action_init' ) ); add_action( 'parse_request', array( $this, 'action_parse_request' ) ); add_action( 'admin_init', array( $this, 'action_admin_init' ) ); } /** * Translate plugin slug. */ public function action_init() { $this->default_slug = __( 'latest', 'eth_redirect_to_latest_post' ); $_slug = get_option( $this->plugin_option_name, $this->default_slug ); if ( is_string( $_slug ) && ! empty( $_slug ) ) { $this->slug = $_slug; } if ( empty( $this->slug ) ) { $this->slug = $this->default_slug; } } /** * Redirect to the latest post any requests made to plugin's slug. * * @param WP $r WP object. */ public function action_parse_request( $r ) { // Nothing to do if permalinks aren't enabled. if ( ! $r->did_permalink ) { return; } $redirect = $this->get_redirect_for_request( $r ); if ( null === $redirect ) { return; } header( 'x-redirect: eth-redirect-to-latest-post' ); // Not validating in case other plugins redirect elsewhere. // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect wp_redirect( $redirect->destination, $redirect->status_code ); exit; } /** * Parse the request to determine its redirect, if any. * * @param WP $r WP object. * @return \stdClass|null */ public function get_redirect_for_request( $r ) { /** * Check if request is for our slug. * * The first condition catches hierarchical permastructs, while * the second catches non-hierarchical permastructs. */ if ( isset( $r->query_vars['pagename'] ) && $this->slug === $r->query_vars['pagename'] ) { $should_intercept = true; } elseif ( isset( $r->query_vars['name'] ) && $this->slug === $r->query_vars['name'] ) { $should_intercept = true; } else { $should_intercept = false; } if ( ! $should_intercept ) { return null; } $redirect = array( 'destination' => '', 'status_code' => 302, ); $query_args = array( 'posts_per_page' => 1, 'post_type' => 'post', 'orderby' => 'date', 'order' => 'desc', 'suppress_filters' => false, 'no_found_rows' => true, ); /** * Filters the query arguments that determine * the latest post. * * @param array $query_args WP_Query arguments. * @param WP $r WP Object. * @return array */ $query_args = apply_filters( 'eth_redirect_to_latest_post_query_args', $query_args, $r ); $latest = get_posts( $query_args ); if ( is_array( $latest ) && ! empty( $latest ) ) { $latest = array_shift( $latest ); $redirect['destination'] = get_permalink( $latest->ID ); } if ( empty( $redirect['destination'] ) ) { $redirect['destination'] = user_trailingslashit( home_url() ); } /** * Filters the redirection data. * * @param array $redirect Array of redirect destination and status code. * @param array|WP_Post $latest Post object or empty array if no posts found. * @param WP $r WP object. * @return array|null */ $redirect = apply_filters( 'eth_redirect_to_latest_post_redirection', $redirect, $latest, $r ); if ( null === $redirect ) { return null; } return (object) $redirect; } /** * ADMIN OPTIONS */ /** * Save plugin settings and register settings field * * Permalinks screen is a snowflake, hence the custom saving handler. */ public function action_admin_init() { // Make sure user has necessary permissions first. if ( ! current_user_can( 'manage_options' ) ) { return; } // Save custom option, permalinks screen is a snowflake and doesn't fully use the Settings API. global $pagenow; if ( 'options-permalink.php' === $pagenow && isset( $_POST[ $this->plugin_option_name ] ) ) { check_admin_referer( 'update-permalink' ); $_slug = sanitize_text_field( $_POST[ $this->plugin_option_name ] ); if ( empty( $_slug ) ) { $_slug = $this->default_slug; } update_option( $this->plugin_option_name, $_slug ); } // Add custom input field to permalinks screen. add_settings_field( $this->plugin_option_name, __( '"Latest post" slug', 'eth_redirect_to_latest_post' ), array( $this, 'settings_field' ), 'permalink', 'optional' ); } /** * Render settings field. */ public function settings_field() { ?> <input type="text" name="<?php echo esc_attr( $this->plugin_option_name ); ?>" value="<?php echo esc_attr( $this->slug ); ?>" class="regular-text" /> <p class="description"> <?php printf( /* translators: 1. Default slug, wrapped in a <code> tag. */ esc_html__( 'Set the slug that will redirect to the latest published post. The default value is %s.', 'eth_redirect_to_latest_post' ), '<code style="font-style: normal;">' . esc_html( $this->default_slug ) . '</code>' ); ?> </p> <?php } } /** * One instance to rule them all. */ ETH_Redirect_To_Latest_Post::get_instance();