Ryan Malloy 0028738e33 Initial commit: TigerStyle Heat v2.0.0
Make your WordPress site irresistible. Natural SEO attraction with:
- robots.txt management
- sitemap.xml generation
- LLMs.txt support
- Google integration (Analytics, Search Console, Tag Manager)
- Schema.org structured data
- Open Graph / Twitter Card meta tags
- AMP support
- Visual elements gallery
- Built-in backup/restore module

Includes build.sh and .distignore for WordPress-installable release ZIPs.
2026-05-27 13:41:35 -06:00

332 lines
12 KiB
PHP

<?php
/**
* AMP (Accelerated Mobile Pages) Module
*
* Handles AMP page generation and Signed Exchange (SXG) support
*
* @package TigerStyle_SEO
* @subpackage Modules
* @since 2.1.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class TigerStyleSEO_AMP {
private $options;
private $cache_dir;
private $sxg_enabled;
/**
* Single instance
*/
private static $instance = null;
/**
* Get instance
*/
public static function instance() {
if (is_null(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
/**
* Constructor
*/
private function __construct() {
$this->options = get_option('tigerstyle_heat_amp', array());
$this->cache_dir = WP_CONTENT_DIR . '/cache/tigerstyle-heat-amp/';
$this->sxg_enabled = isset($this->options['sxg_enabled']) ? $this->options['sxg_enabled'] : false;
$this->init();
}
/**
* Initialize the module
*/
private function init() {
// Create cache directory
if (!file_exists($this->cache_dir)) {
wp_mkdir_p($this->cache_dir);
}
// AMP detection and generation
add_action('template_redirect', array($this, 'handle_amp_request'));
add_action('wp_head', array($this, 'add_amp_link'));
// SXG support
if ($this->sxg_enabled) {
add_action('template_redirect', array($this, 'handle_sxg_request'), 5);
add_filter('wp_headers', array($this, 'add_sxg_headers'));
}
// Admin hooks
if (is_admin()) {
add_action('admin_post_update_amp_settings', array($this, 'handle_form_submission'));
}
}
/**
* Handle AMP requests
*/
public function handle_amp_request() {
if (!isset($_GET['amp']) || is_admin()) {
return;
}
// Generate AMP page
$this->generate_amp_page();
exit;
}
/**
* Add AMP link to head
*/
public function add_amp_link() {
if (is_singular() && !is_admin()) {
$amp_url = add_query_arg('amp', '1', get_permalink());
echo '<link rel="amphtml" href="' . esc_url($amp_url) . '">' . "\n";
}
}
/**
* Generate AMP page
*/
private function generate_amp_page() {
// Basic AMP page structure
?>
<!doctype html>
<html amp lang="<?php echo get_locale(); ?>">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title><?php wp_title(); ?></title>
<script async src="https://cdn.ampproject.org/v0.js"></script>
<style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style><noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
<style amp-custom>
body { font-family: sans-serif; margin: 20px; }
h1 { color: #333; }
p { line-height: 1.6; }
</style>
</head>
<body>
<h1><?php the_title(); ?></h1>
<div class="content">
<?php
if (have_posts()) {
while (have_posts()) {
the_post();
the_content();
}
}
?>
</div>
</body>
</html>
<?php
}
/**
* Handle SXG requests
*/
public function handle_sxg_request() {
if (!isset($_GET['sxg']) || !$this->sxg_enabled) {
return;
}
// Check if SXG service is available
if (!$this->is_sxg_service_available()) {
wp_die('SXG service not available', 'Service Unavailable', ['response' => 503]);
}
$this->serve_signed_exchange();
exit;
}
/**
* Check if SXG service is available
*/
private function is_sxg_service_available() {
return !empty($this->options['sxg_api_key']) && !empty($this->options['sxg_api_endpoint']);
}
/**
* Serve Signed Exchange
*/
public function serve_signed_exchange() {
$current_url = $this->get_current_url();
// Check cache first
$cache_key = 'sxg_' . md5($current_url);
$cached_sxg = get_transient($cache_key);
if ($cached_sxg !== false) {
header('Content-Type: application/signed-exchange;v=b3');
header('Cache-Control: public, max-age=300');
echo $cached_sxg;
return;
}
// Generate fresh SXG
ob_start();
$this->generate_amp_page();
$amp_content = ob_get_clean();
$sxg_data = $this->create_signed_exchange($amp_content, $current_url);
if ($sxg_data) {
// Cache for 5 minutes
set_transient($cache_key, $sxg_data, 300);
header('Content-Type: application/signed-exchange;v=b3');
header('Cache-Control: public, max-age=300');
echo $sxg_data;
} else {
wp_die('Failed to create signed exchange', 'SXG Error', ['response' => 500]);
}
}
/**
* Create signed exchange using API
*/
private function create_signed_exchange($content, $url) {
$api_endpoint = $this->options['sxg_api_endpoint'];
$api_key = $this->options['sxg_api_key'];
$response = wp_remote_post($api_endpoint, array(
'headers' => array(
'Authorization' => 'Bearer ' . $api_key,
'Content-Type' => 'application/json'
),
'body' => json_encode(array(
'url' => $url,
'content' => $content,
'headers' => array(
'content-type' => 'text/html; charset=utf-8'
)
)),
'timeout' => 30
));
if (is_wp_error($response)) {
error_log('TigerStyle Heat: SXG API error: ' . $response->get_error_message());
return false;
}
$body = wp_remote_retrieve_body($response);
$data = json_decode($body, true);
if (isset($data['sxg_package'])) {
return base64_decode($data['sxg_package']);
}
return false;
}
/**
* Get current URL
*/
private function get_current_url() {
return (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
}
/**
* Add SXG headers
*/
public function add_sxg_headers($headers) {
if ($this->sxg_enabled && isset($_GET['amp'])) {
$headers['Link'] = '</wp-content/cache/tigerstyle-heat-amp/sxg-cert.pem>; rel="alternate"; type="application/cert-chain+cbor"';
$headers['Vary'] = 'Accept, AMP-Cache-Transform';
}
return $headers;
}
/**
* Handle form submission
*/
public function handle_form_submission() {
if (!isset($_POST['amp_nonce']) || !wp_verify_nonce($_POST['amp_nonce'], 'update_amp_settings')) {
wp_die('Security check failed');
}
$options = array();
$options['sxg_enabled'] = isset($_POST['sxg_enabled']) ? 1 : 0;
$options['sxg_api_endpoint'] = sanitize_url($_POST['sxg_api_endpoint']);
$options['sxg_api_key'] = sanitize_text_field($_POST['sxg_api_key']);
update_option('tigerstyle_heat_amp', $options);
wp_redirect(admin_url('admin.php?page=tigerstyle-heat#amp-tab&updated=1'));
exit;
}
/**
* Render admin page
*/
public function render_admin_page() {
$options = $this->options;
?>
<div class="seo-info-box">
<h3><?php _e('AMP & SXG Configuration', 'tigerstyle-heat'); ?></h3>
<p class="description">
<?php _e('Configure AMP (Accelerated Mobile Pages) and SXG (Signed Exchange) support for faster mobile loading.', 'tigerstyle-heat'); ?>
</p>
<form method="post" action="<?php echo admin_url('admin-post.php'); ?>">
<input type="hidden" name="action" value="update_amp_settings">
<?php wp_nonce_field('update_amp_settings', 'amp_nonce'); ?>
<table class="form-table">
<tr>
<th scope="row"><?php _e('Enable SXG Support', 'tigerstyle-heat'); ?></th>
<td>
<label>
<input type="checkbox" name="sxg_enabled" value="1" <?php checked(isset($options['sxg_enabled']) ? $options['sxg_enabled'] : 0, 1); ?>>
<?php _e('Enable Signed Exchange support for AMP pages', 'tigerstyle-heat'); ?>
</label>
<p class="description"><?php _e('Requires valid SXG API credentials.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('SXG API Endpoint', 'tigerstyle-heat'); ?></th>
<td>
<input type="url" name="sxg_api_endpoint" value="<?php echo esc_attr(isset($options['sxg_api_endpoint']) ? $options['sxg_api_endpoint'] : ''); ?>" class="regular-text">
<p class="description"><?php _e('SXG API service endpoint URL.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
<tr>
<th scope="row"><?php _e('SXG API Key', 'tigerstyle-heat'); ?></th>
<td>
<input type="password" name="sxg_api_key" value="<?php echo esc_attr(isset($options['sxg_api_key']) ? $options['sxg_api_key'] : ''); ?>" class="regular-text">
<p class="description"><?php _e('API key for SXG service authentication.', 'tigerstyle-heat'); ?></p>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="submit" class="button button-primary" value="<?php _e('Save AMP Settings', 'tigerstyle-heat'); ?>">
</p>
</form>
</div>
<div class="seo-info-box">
<h4><?php _e('AMP Status', 'tigerstyle-heat'); ?></h4>
<p>
<?php _e('AMP pages are automatically generated for all posts and pages.', 'tigerstyle-heat'); ?>
<?php if (is_singular()): ?>
<br><strong><?php _e('Current page AMP URL:', 'tigerstyle-heat'); ?></strong>
<a href="<?php echo add_query_arg('amp', '1', get_permalink()); ?>" target="_blank">
<?php echo add_query_arg('amp', '1', get_permalink()); ?>
</a>
<?php endif; ?>
</p>
</div>
<?php
}
}