<?php
namespace wpbuddy\rich_snippets;
if ( ! defined( 'ABSPATH' ) ) {
exit;
} // Exit if accessed directly
/**
* Class Admin.
*
* Starts up all the frontend things.
*
* @package wpbuddy\rich_snippets
*
* @since 2.0.0
*/
class Frontend_Controller {
/**
* If debug mode is on|off.
*
* @since 2.0.0
*
* @var bool
*/
protected $debug = false;
/**
* If frontend caching is active.
*
* @since 2.11.0
*
* @var bool
*/
protected $caching = true;
/**
* The caching time in hours.
*
* @since 2.11.0
*
* @var int
*/
protected $caching_time = 6;
/**
* Current post ID.
*
* @since 2.0.0
*
* @var int
*/
protected $current_post_id = 0;
/**
* If Values_Model has been initialized.
*
* @since 2.2.5
*
* @var bool
*/
protected $values_model_initialized = false;
/**
* Magic method for setting up the class.
*
* @since 2.0.0
*/
public function __construct() {
$this->debug = defined( 'WPB_RS_DEBUG' ) && WPB_RS_DEBUG;
$this->caching = (bool) get_option( 'wpb_rs/setting/frontend_caching', false ) && ! $this->debug;
$this->caching_time = intval( get_option( 'wpb_rs/setting/frontend_caching_time', 6 ) );
if ( false !== stripos( get_class( $this ), 'rich_snippets\\pro\\' ) ) {
if ( ! get_option( 'wpb_rs/verified', false ) ) {
return;
}
} else {
if ( ! get_option( 'wpb_rs/active', false ) ) {
return;
}
}
add_action( 'wp', array( $this, 'set_object_vars' ), 10, 1 );
if ( (bool) get_option( 'wpb_rs/setting/snippets_in_footer', true ) ) {
add_action( 'wp_footer', array( $this, 'print_snippets' ) );
add_action( 'amp_post_template_head', array( $this, 'print_snippets' ) );
} else {
add_action( 'wp_head', array( $this, 'print_snippets' ) );
add_action( 'amp_post_template_footer', array( $this, 'print_snippets' ) );
}
add_filter( 'post_class', array( $this, 'remove_hentry' ) );
add_filter( 'comment_class', array( $this, 'remove_vcard' ) );
add_action( 'woocommerce_init', array( $this, 'remove_wc_structured_data' ) );
add_action( 'init', array( $this, 'remove_yoast_structured_data' ) );
add_action( 'admin_bar_menu', array( $this, 'check_page_menu' ), 150 );
/**
* Frontend init hook.
*
* Allow other plugins to perform any actions.
*
* @hook wpbuddy/rich_snippets/frontend/init
*
* @param {Frontend_Controller} $frontend
*
* @since 2.0.0
*
*/
do_action_ref_array( 'wpbuddy/rich_snippets/frontend/init', array( &$this ) );
}
/**
* Set objects vars of this class.
*
* @param \WP $wp
*
* @since 2.1.1
*
*/
public function set_object_vars( $wp ) {
/**
* Fetch the current post ID.
*/
$this->current_post_id = Helper_Model::instance()->get_current_post_id();
}
/**
* Prints the schema.
*
* @since 2.0.0
*/
public function print_snippets() {
if ( (bool) get_post_meta( $this->current_post_id, 'snip_hide_schemas', true ) ) {
printf(
'<!--%s-->',
__( 'SNIP INFO: All schemas are disabled on this page. Uncheck the "Hide all schemas on this post" checkbox on this post to resolve the issue.', 'rich-snippets-schema' )
);
return;
}
/**
* Check for global snippets.
*/
if ( $this->have_global_snippets() ) {
# Run through all snippets.
foreach ( $this->get_global_snippet_post_ids() as $global_snippet_post_id ) {
if ( ! $this->is_global_snippet_active( $global_snippet_post_id ) ) {
continue;
}
$this->print_rich_snippets( $global_snippet_post_id );
}
}
/**
* Check for snippets attached to a single post.
*/
if ( $this->singular_has_snippet( $this->current_post_id ) ) {
$this->print_rich_snippets( $this->current_post_id );
}
}
/**
* Prints rich snippets.
*
* @param int $post_id The post ID where snippets were defined.
*
* @since 2.0.0
*/
public function print_rich_snippets( int $post_id ) {
echo '<!--';
_e(
'Code generated by SNIP (Structured Data Plugin) for WordPress. See rich-snippets.io for more information.',
'rich-snippets-schema'
);
printf( __( 'Post ID is %d.', 'rich-snippets-schema' ), $post_id );
echo '-->';
if ( $this->caching ) {
$cache = get_transient( Cache_Model::get_cache_key() );
} else {
$cache = [];
}
if ( is_array( $cache )
&& isset( $cache[ $post_id ] )
&& isset( $cache[ $post_id ]['snippets'] )
&& is_array( $cache[ $post_id ]['snippets'] )
) {
echo implode( '', $cache[ $post_id ]['snippets'] );
return;
}
if ( ! $this->values_model_initialized ) {
# Init value hooks
$this->init_values_model();
}
$rich_snippets = Snippets_Model::get_snippets( $post_id );
$cache = ! is_array( $cache ) ? [] : $cache;
foreach ( $rich_snippets as $snippet_uid => $rich_snippet ) {
$rich_snippet->prepare_for_output( array(
'current_post_id' => $this->current_post_id,
'snippet_post_id' => $post_id,
'input' => 'snippet_' . $post_id . '_',
'queried_object' => get_queried_object(),
) );
if ( ! $rich_snippet->has_properties() ) {
continue;
}
$output = sprintf(
'<script data-snippet_id="%s" type="application/ld+json">%s</script>',
esc_attr( $snippet_uid ),
$rich_snippet->__toString()
);
$cache[ $post_id ]['snippets'][ $snippet_uid ] = $output;
echo $output;
}
if ( $this->caching ) {
set_transient( Cache_Model::get_cache_key(), $cache, $this->caching_time * HOUR_IN_SECONDS );
}
}
/**
* Checks if a singular post has snippets.
*
* @param int $post_id
*
* @return bool
* @since 2.0.0
*
*/
public function singular_has_snippet( $post_id ): bool {
return count( Snippets_Model::get_snippets( $post_id ) ) > 0;
}
/**
* Removes "hentry" class from post classes.
*
* @param array $classes
*
* @return array
* @since 2.0.0
*
*/
public function remove_hentry( $classes ) {
$k = array_search( 'hentry', $classes );
if ( false !== $k && (bool) get_option( 'wpb_rs/setting/remove_hentry', true ) ) {
unset( $classes[ $k ] );
}
return $classes;
}
/**
* Removes vcard CSS classes from comments.
*
* @param array $classes
*
* @return array
* @since 2.0.0
*
*/
public function remove_vcard( $classes ) {
$k = array_search( 'vcard', $classes );
if ( false !== $k && (bool) get_option( 'wpb_rs/settings/remove_vcard', true ) ) {
unset( $classes[ $k ] );
}
return $classes;
}
/**
* Maybe deactivates WooCommerce structured data.
*
* @since 2.0.0
*/
public function remove_wc_structured_data() {
$deactivate = (bool) get_option( 'wpb_rs/setting/remove_wc_schema', false );
if ( $deactivate ) {
remove_action( 'wp_footer', array( WC()->structured_data, 'output_structured_data' ), 10 );
}
}
/**
* Remove Structured Data generated by Yoast SEO.
*
* @since 2.11.0
*/
public function remove_yoast_structured_data() {
$deactivate = (bool) get_option( 'wpb_rs/setting/remove_yoast_schema', false );
if ( $deactivate ) {
add_filter( 'wpseo_json_ld_output', '__return_false' );
}
}
/**
* Initializes values model.
*
* @since 2.19.0
*/
public function init_values_model() {
if ( ! $this->values_model_initialized ) {
new Values_Model();
}
$this->values_model_initialized = true;
}
/**
* Add new admin bar menu option.
*
* @param \WP_Admin_Bar $wp_admin_bar
*
* @since 2.1.9.0
*/
public function check_page_menu( $wp_admin_bar ) {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$wp_admin_bar->add_menu( array(
'id' => 'snip-test-link',
'parent' => null,
'group' => null,
'title' => __( 'Test Structured Data', 'rich-snippets-schema' ),
'href' => add_query_arg( [
'url' => ( is_ssl() ? 'https' : 'http' ) . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'],
'user_agent' => 2,
], 'https://search.google.com/test/rich-results' ),
'meta' => [
'title' => __( 'Test the current pages Structured Data', 'rich-snippets-schema' ),
'target' => '_blank'
]
) );
}
/**
* Checks if the current page has any global snippets.
*
* @return bool
* @since 2.0.0
*
*/
public function have_global_snippets(): bool {
return count( $this->get_global_snippet_post_ids() ) > 0;
}
/**
* Returns the global schema post IDs.
*
* @return int[] Array with post Ids.
* @since 2.0.0
*
*/
public function get_global_snippet_post_ids(): array {
if ( $this->caching ) {
$cache = get_transient( 'wpb_rs/cache/global_snippets_ids' );
if ( is_array( $cache ) ) {
return $cache;
}
}
$query = new \WP_Query( array(
'post_type' => 'wpb-rs-global',
'fields' => 'ids',
'post_status' => 'publish',
'posts_per_page' => - 1,
) );
if ( ! $query->have_posts() ) {
if ( $this->caching ) {
set_transient( 'wpb_rs/cache/global_snippets_ids', array(), $this->caching_time * HOUR_IN_SECONDS );
}
return array();
}
$ids = $query->get_posts();
if ( $this->caching ) {
set_transient( 'wpb_rs/cache/global_snippets_ids', $ids, $this->caching_time * HOUR_IN_SECONDS );
}
return $ids;
}
/**
* Checks the rules.
*
* @param int $id
*
* @return bool
* @since 2.0.0
* @since 2.8.0 renamed from is_schema_active()
*
*/
public function is_global_snippet_active( int $id ): bool {
$cache_key = Cache_Model::get_cache_key();
if ( $this->caching ) {
$cache = get_transient( $cache_key );
}
if ( isset( $cache )
&& is_array( $cache )
&& isset( $cache[ $id ] )
&& isset( $cache[ $id ]['match_result'] )
) {
/**
* Cached match result filter.
*
* Allows to change the cached value of a matching rule.
*
* @hook wpb_rs/cache/rule_{$id}
*
* @param {bool} $match_result The match result from the cache.
* @param {bool} $default_result The default result.
*
* @since 2.0.0
*
* @returns {bool}
*/
$value = apply_filters( 'wpb_rs/cache/rule_' . $id, $cache[ $id ]['match_result'], true );
return boolval( $value );
}
$cache = [];
$match_result = \wpbuddy\rich_snippets\Rules_Model::get_ruleset( $id )->match();
$cache[ $id ]['match_result'] = $match_result;
if ( $this->caching ) {
set_transient( $cache_key, $cache, $this->caching_time * HOUR_IN_SECONDS );
}
return boolval( apply_filters( 'wpb_rs/cache/rule_' . $id, $match_result, false ) );
}
}