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

842 lines
32 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* AMP (Accelerated Mobile Pages) Admin Page
*
* @package TigerStyle_SEO
* @subpackage Admin
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
$amp_options = get_option('tigerstyle_heat_amp', []);
$amp_enabled = $amp_options['enable_amp'] ?? false;
$sxg_preparation = $amp_options['sxg_preparation'] ?? false;
// Infrastructure check - get module from plugin instance
$amp_module = tigerstyle_heat()->get_module('amp');
$sxg_status = ($amp_module && method_exists($amp_module, 'check_infrastructure_status')) ? $amp_module->check_infrastructure_status() : [];
?>
<div class="tigerstyle-heat-admin-content">
<form method="post" action="options.php" id="tigerstyle-heat-amp-form">
<?php settings_fields('tigerstyle_heat_amp'); ?>
<!-- AMP Configuration -->
<div class="tigerstyle-heat-card">
<div class="card-header">
<h3>⚡ AMP Configuration</h3>
<p>Accelerated Mobile Pages for faster loading on mobile devices</p>
</div>
<div class="card-content">
<div class="form-group">
<label class="toggle-switch">
<input type="checkbox"
name="tigerstyle_heat_amp[enable_amp]"
value="1"
<?php checked($amp_enabled); ?>>
<span class="toggle-slider"></span>
<span class="toggle-label">Enable AMP Pages</span>
</label>
<p class="description">Generate AMP versions of your content for faster mobile loading</p>
</div>
<?php if ($amp_enabled): ?>
<div class="form-group">
<label for="amp_post_types">Enable AMP for Post Types:</label>
<div class="checkbox-group">
<?php
$post_types = get_post_types(['public' => true], 'objects');
$enabled_types = $amp_options['post_types'] ?? ['post'];
foreach ($post_types as $post_type): ?>
<label class="checkbox-label">
<input type="checkbox"
name="tigerstyle_heat_amp[post_types][]"
value="<?php echo esc_attr($post_type->name); ?>"
<?php checked(in_array($post_type->name, $enabled_types)); ?>>
<?php echo esc_html($post_type->label); ?>
</label>
<?php endforeach; ?>
</div>
</div>
<div class="form-group">
<label for="amp_publisher_logo">Publisher Logo URL:</label>
<input type="url"
id="amp_publisher_logo"
name="tigerstyle_heat_amp[publisher_logo]"
value="<?php echo esc_attr($amp_options['publisher_logo'] ?? ''); ?>"
placeholder="https://example.com/logo.png"
class="regular-text">
<p class="description">Logo for structured data (600×60px recommended)</p>
</div>
<div class="form-group">
<label for="amp_analytics_id">Google Analytics ID:</label>
<input type="text"
id="amp_analytics_id"
name="tigerstyle_heat_amp[analytics_id]"
value="<?php echo esc_attr($amp_options['analytics_id'] ?? ''); ?>"
placeholder="GA-XXXXXXXX-X"
class="regular-text">
<p class="description">Google Analytics tracking ID for AMP pages</p>
</div>
<?php endif; ?>
</div>
</div>
<!-- Signed Exchange (SXG) Section -->
<div class="tigerstyle-heat-card">
<div class="card-header">
<h3>🔐 Signed Exchange (SXG) Support</h3>
<p>Enable AMP pages to be served from original domain while cached by Google</p>
</div>
<div class="card-content">
<!-- Infrastructure Requirements -->
<div class="sxg-requirements">
<h4>Infrastructure Requirements</h4>
<div class="requirements-grid">
<?php
$requirements = [
'amppackager_binary' => [
'title' => 'amppackager Binary',
'description' => 'Go binary installation required',
'status' => $sxg_status['amppackager_binary'] ?? false,
'docs' => 'https://github.com/ampproject/amppackager'
],
'certificate_authority' => [
'title' => 'Supported SSL Certificate',
'description' => 'Certificate from supported CA (Let\'s Encrypt, DigiCert, etc.)',
'status' => $sxg_status['certificate_valid'] ?? false,
'docs' => 'https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/signed-exchange/#certificates'
],
'server_control' => [
'title' => 'Edge Server Control',
'description' => 'Ability to control HTTP headers at server level',
'status' => $sxg_status['server_control'] ?? false,
'docs' => 'https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/signed-exchange/#server-requirements'
],
'network_connectivity' => [
'title' => 'Network Access',
'description' => 'Outgoing requests to CA, publisher, cdn.ampproject.org',
'status' => $sxg_status['network_connectivity'] ?? false,
'docs' => null
]
];
foreach ($requirements as $key => $requirement): ?>
<div class="requirement-item">
<div class="requirement-status <?php echo $requirement['status'] ? 'met' : 'not-met'; ?>">
<?php if ($requirement['status']): ?>
<span class="dashicons dashicons-yes-alt"></span>
<?php else: ?>
<span class="dashicons dashicons-warning"></span>
<?php endif; ?>
</div>
<div class="requirement-details">
<h5><?php echo esc_html($requirement['title']); ?></h5>
<p><?php echo esc_html($requirement['description']); ?></p>
<?php if ($requirement['docs']): ?>
<a href="<?php echo esc_url($requirement['docs']); ?>" target="_blank" class="docs-link">
Documentation →
</a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- SXG Preparation -->
<div class="sxg-preparation">
<h4>WordPress-Level Preparation</h4>
<p>This plugin can prepare your WordPress site for SXG, but cannot implement the full SXG infrastructure.</p>
<div class="form-group">
<label class="toggle-switch">
<input type="checkbox"
name="tigerstyle_heat_amp[sxg_preparation]"
value="1"
<?php checked($sxg_preparation); ?>>
<span class="toggle-slider"></span>
<span class="toggle-label">Enable SXG Preparation</span>
</label>
<p class="description">Add SXG-compatible headers and meta tags (WordPress level only)</p>
</div>
<?php if ($sxg_preparation): ?>
<div class="sxg-actions">
<button type="button" class="button" id="test-sxg-infrastructure">
Test Infrastructure
</button>
<button type="button" class="button" id="generate-amppackager-config">
Generate amppackager Config
</button>
</div>
<?php endif; ?>
</div>
<!-- Infrastructure Setup Guide -->
<!-- Binary Execution Test Section -->
<div class="binary-test-section">
<h4>Environment Testing</h4>
<p>Test if your server environment can run amppackager binary before proceeding with setup.</p>
<div class="test-controls">
<button type="button" class="button" id="test-binary-execution">
Test Binary Execution
</button>
<div id="test-results" class="test-results" style="display: none;"></div>
</div>
</div>
<!-- Binary Upload Section -->
<div class="binary-upload-section" id="binary-upload-section" style="display: none;">
<h4>amppackager Binary Upload</h4>
<p>Upload the amppackager binary to run locally (avoids security concerns of automatic downloads).</p>
<div class="upload-controls">
<div class="form-group">
<label for="amppackager-binary">Select amppackager Binary:</label>
<input type="file" id="amppackager-binary" name="amppackager_binary" accept=".exe,*">
<p class="description">
Download from: <a href="https://github.com/ampproject/amppackager/releases/latest" target="_blank">GitHub Releases</a>
<br>Choose the appropriate version for your server architecture.
</p>
</div>
<button type="button" class="button button-primary" id="upload-test-binary">
Upload & Test Binary
</button>
<button type="button" class="button" id="configure-external-api">
Use External SXG API Instead
</button>
</div>
</div>
<!-- External API Configuration -->
<div class="api-config-section" id="api-config-section" style="display: none;">
<h4>External SXG API Configuration</h4>
<p>Configure external API for SXG generation when binary execution is not available.</p>
<div class="form-group">
<label for="sxg_api_url">SXG API Endpoint:</label>
<input type="url"
id="sxg_api_url"
name="tigerstyle_heat_amp[sxg_api_url]"
value="<?php echo esc_attr($amp_options['sxg_api_url'] ?? ''); ?>"
placeholder="https://your-sxg-api.com/generate"
class="regular-text">
</div>
<div class="form-group">
<label for="sxg_api_key">API Key:</label>
<input type="password"
id="sxg_api_key"
name="tigerstyle_heat_amp[sxg_api_key]"
value="<?php echo esc_attr($amp_options['sxg_api_key'] ?? ''); ?>"
placeholder="Your API key"
class="regular-text">
</div>
<button type="button" class="button" id="test-sxg-api">
Test API Connection
</button>
</div>
<div class="sxg-setup-guide">
<h4>Setting Up Full SXG Support</h4>
<div class="setup-steps">
<div class="setup-step">
<h5>1. Server Requirements</h5>
<ul>
<li>Linux server with root access (not shared hosting)</li>
<li>Go runtime environment installed</li>
<li>Ability to configure web server (Apache/Nginx)</li>
<li>SSL certificate from supported CA</li>
</ul>
</div>
<div class="setup-step">
<h5>2. Install amppackager</h5>
<div class="code-block">
<pre><code># Download and install amppackager
wget https://github.com/ampproject/amppackager/releases/download/latest/amppackager
chmod +x amppackager
sudo mv amppackager /usr/local/bin/
# Create config directory
sudo mkdir -p /etc/amppackager</code></pre>
</div>
</div>
<div class="setup-step">
<h5>3. Configure Web Server</h5>
<p>Configure your web server to:</p>
<ul>
<li>Vary responses on <code>Accept</code> and <code>AMP-Cache-Transform</code> headers</li>
<li>Proxy SXG requests to amppackager</li>
<li>Serve different content for the same URL based on headers</li>
</ul>
</div>
<div class="setup-step">
<h5>4. Maintenance</h5>
<ul>
<li>Update amppackager every 6 weeks</li>
<li>Monitor certificate expiration</li>
<li>Ensure persistent storage for amppackager instances</li>
</ul>
</div>
</div>
</div>
<!-- Compatible Hosting -->
<div class="compatible-hosting">
<h4>Compatible Hosting Solutions</h4>
<div class="hosting-grid">
<div class="hosting-option compatible">
<h5>✅ VPS/Dedicated Servers</h5>
<p>Full control over server configuration</p>
<ul>
<li>DigitalOcean</li>
<li>Linode</li>
<li>AWS EC2</li>
<li>Google Cloud</li>
</ul>
</div>
<div class="hosting-option partial">
<h5>⚠️ Managed WordPress</h5>
<p>May require hosting provider support</p>
<ul>
<li>WP Engine (contact support)</li>
<li>Kinsta (enterprise plans)</li>
<li>Pantheon (custom)</li>
</ul>
</div>
<div class="hosting-option incompatible">
<h5>❌ Shared Hosting</h5>
<p>Cannot install binaries or control headers</p>
<ul>
<li>Most shared hosting providers</li>
<li>Blogger</li>
<li>WordPress.com</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<!-- AMP Testing Tools -->
<div class="tigerstyle-heat-card">
<div class="card-header">
<h3>🔧 Testing & Validation</h3>
<p>Tools to test and validate your AMP implementation</p>
</div>
<div class="card-content">
<div class="testing-tools">
<div class="tool-group">
<h4>AMP Validation</h4>
<a href="https://validator.ampproject.org/" target="_blank" class="button button-secondary">
AMP Validator →
</a>
<p>Test your AMP pages for compliance</p>
</div>
<div class="tool-group">
<h4>Google Search Console</h4>
<a href="https://search.google.com/search-console" target="_blank" class="button button-secondary">
Search Console →
</a>
<p>Monitor AMP status and errors</p>
</div>
<div class="tool-group">
<h4>AMP Test</h4>
<a href="https://search.google.com/test/amp" target="_blank" class="button button-secondary">
Google AMP Test →
</a>
<p>Test AMP pages in Google's tools</p>
</div>
<div class="tool-group">
<h4>SXG Documentation</h4>
<a href="https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/signed-exchange" target="_blank" class="button button-secondary">
AMP SXG Guide →
</a>
<a href="https://wicg.github.io/webpackage/draft-yasskin-httpbis-origin-signed-exchanges-impl.html" target="_blank" class="button button-secondary">
SXG Implementation →
</a>
<p>Official documentation and implementation details</p>
</div>
</div>
</div>
</div>
<?php submit_button('Save AMP Settings', 'primary', 'submit', false); ?>
</form>
</div>
<style>
.sxg-requirements {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
}
.requirements-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-top: 15px;
}
.requirement-item {
display: flex;
align-items: flex-start;
gap: 12px;
padding: 15px;
background: white;
border-radius: 6px;
border-left: 4px solid #ddd;
}
.requirement-item.met {
border-left-color: #46b450;
}
.requirement-item.not-met {
border-left-color: #dc3232;
}
.requirement-status .dashicons {
font-size: 20px;
margin-top: 2px;
}
.requirement-status .dashicons-yes-alt {
color: #46b450;
}
.requirement-status .dashicons-warning {
color: #dc3232;
}
.requirement-details h5 {
margin: 0 0 8px 0;
font-weight: 600;
}
.requirement-details p {
margin: 0 0 8px 0;
color: #666;
font-size: 13px;
}
.docs-link {
font-size: 12px;
color: #0073aa;
text-decoration: none;
}
.setup-steps {
display: grid;
gap: 25px;
margin-top: 15px;
}
.setup-step {
padding: 20px;
background: white;
border-radius: 6px;
border-left: 4px solid #0073aa;
}
.setup-step h5 {
margin: 0 0 15px 0;
color: #0073aa;
font-weight: 600;
}
.setup-step ul {
margin: 10px 0;
padding-left: 20px;
}
.code-block {
background: #2c3e50;
color: #ecf0f1;
padding: 15px;
border-radius: 4px;
margin: 10px 0;
overflow-x: auto;
}
.code-block pre {
margin: 0;
font-family: 'Courier New', monospace;
font-size: 13px;
line-height: 1.4;
}
.hosting-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-top: 15px;
}
.hosting-option {
padding: 20px;
border-radius: 6px;
border: 2px solid #ddd;
}
.hosting-option.compatible {
border-color: #46b450;
background: #f7fff7;
}
.hosting-option.partial {
border-color: #ffb900;
background: #fffbf0;
}
.hosting-option.incompatible {
border-color: #dc3232;
background: #fff7f7;
}
.hosting-option h5 {
margin: 0 0 10px 0;
}
.hosting-option ul {
margin: 10px 0 0 0;
padding-left: 20px;
}
.testing-tools {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
}
.tool-group {
text-align: center;
padding: 20px;
background: #f8f9fa;
border-radius: 6px;
}
.tool-group h4 {
margin: 0 0 15px 0;
}
.tool-group p {
margin: 10px 0 0 0;
font-size: 13px;
color: #666;
}
.sxg-actions {
margin: 20px 0;
}
.sxg-actions .button {
margin-right: 10px;
}
.binary-test-section,
.binary-upload-section,
.api-config-section {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
border-left: 4px solid #0073aa;
}
.test-controls,
.upload-controls {
margin-top: 15px;
}
.test-results {
margin-top: 15px;
padding: 15px;
border-radius: 4px;
font-family: monospace;
font-size: 12px;
line-height: 1.4;
}
.test-results.success {
background: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.test-results.error {
background: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.test-results.warning {
background: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
}
.upload-controls .form-group {
margin-bottom: 15px;
}
.upload-controls .button {
margin-right: 10px;
margin-bottom: 10px;
}
.tool-group a.button {
margin: 5px;
display: inline-block;
}
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Test Binary Execution
document.getElementById('test-binary-execution')?.addEventListener('click', function() {
const button = this;
const resultsDiv = document.getElementById('test-results');
button.textContent = 'Testing...';
button.disabled = true;
resultsDiv.style.display = 'block';
resultsDiv.className = 'test-results';
resultsDiv.innerHTML = 'Running binary execution tests...';
fetch(ajaxurl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
action: 'tigerstyle_heat_test_binary_execution',
_ajax_nonce: '<?php echo wp_create_nonce('tigerstyle_heat_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
const results = data.data;
let resultHtml = '<strong>Binary Execution Test Results:</strong><br>';
resultHtml += `exec() function available: ${results.exec_function ? '✅ YES' : '❌ NO'}<br>`;
resultHtml += `exec() not disabled: ${results.exec_allowed ? '✅ YES' : '❌ NO'}<br>`;
resultHtml += `Temporary directory writable: ${results.temp_writable ? '✅ YES' : '❌ NO'}<br>`;
resultHtml += `Binary execution test: ${results.binary_test ? '✅ SUCCESS' : '❌ FAILED'}<br>`;
if (results.error_message) {
resultHtml += `<br><strong>Error:</strong> ${results.error_message}`;
}
resultsDiv.innerHTML = resultHtml;
if (results.exec_function && results.exec_allowed && results.temp_writable && results.binary_test) {
resultsDiv.className = 'test-results success';
document.getElementById('binary-upload-section').style.display = 'block';
} else {
resultsDiv.className = 'test-results error';
document.getElementById('api-config-section').style.display = 'block';
}
} else {
resultsDiv.innerHTML = `<strong>Test Failed:</strong> ${data.data.message || 'Unknown error'}`;
resultsDiv.className = 'test-results error';
document.getElementById('api-config-section').style.display = 'block';
}
})
.catch(error => {
resultsDiv.innerHTML = `<strong>Test Failed:</strong> ${error.message}`;
resultsDiv.className = 'test-results error';
document.getElementById('api-config-section').style.display = 'block';
})
.finally(() => {
button.textContent = 'Test Binary Execution';
button.disabled = false;
});
});
// Configure External API
document.getElementById('configure-external-api')?.addEventListener('click', function() {
document.getElementById('binary-upload-section').style.display = 'none';
document.getElementById('api-config-section').style.display = 'block';
});
// Test SXG API
document.getElementById('test-sxg-api')?.addEventListener('click', function() {
const button = this;
const apiUrl = document.getElementById('sxg_api_url').value;
const apiKey = document.getElementById('sxg_api_key').value;
if (!apiUrl) {
alert('Please enter an API endpoint URL');
return;
}
button.textContent = 'Testing...';
button.disabled = true;
fetch(ajaxurl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
action: 'tigerstyle_heat_test_sxg_api',
api_url: apiUrl,
api_key: apiKey,
_ajax_nonce: '<?php echo wp_create_nonce('tigerstyle_heat_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('API connection successful! Response: ' + JSON.stringify(data.data.response));
} else {
alert('API test failed: ' + data.data.message);
}
})
.catch(error => {
alert('API test failed: ' + error.message);
})
.finally(() => {
button.textContent = 'Test API Connection';
button.disabled = false;
});
});
// Upload & Test Binary
document.getElementById('upload-test-binary')?.addEventListener('click', function() {
const button = this;
const fileInput = document.getElementById('amppackager-binary');
const file = fileInput.files[0];
if (!file) {
alert('Please select a binary file to upload');
return;
}
button.textContent = 'Uploading...';
button.disabled = true;
const formData = new FormData();
formData.append('action', 'tigerstyle_heat_upload_binary');
formData.append('binary_file', file);
formData.append('_ajax_nonce', '<?php echo wp_create_nonce('tigerstyle_heat_nonce'); ?>');
fetch(ajaxurl, {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('Binary uploaded and tested successfully! ' + data.data.message);
} else {
alert('Binary upload failed: ' + data.data.message);
}
})
.catch(error => {
alert('Binary upload failed: ' + error.message);
})
.finally(() => {
button.textContent = 'Upload & Test Binary';
button.disabled = false;
});
});
// Test SXG Infrastructure
document.getElementById('test-sxg-infrastructure')?.addEventListener('click', function() {
this.textContent = 'Testing...';
this.disabled = true;
fetch(ajaxurl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
action: 'tigerstyle_heat_test_infrastructure',
_ajax_nonce: '<?php echo wp_create_nonce('tigerstyle_heat_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
alert('Infrastructure test completed. Check console for details.');
console.log('SXG Infrastructure Test Results:', data);
})
.catch(error => {
alert('Test failed: ' + error.message);
})
.finally(() => {
this.textContent = 'Test Infrastructure';
this.disabled = false;
});
});
// Generate amppackager config
document.getElementById('generate-amppackager-config')?.addEventListener('click', function() {
this.textContent = 'Generating...';
this.disabled = true;
fetch(ajaxurl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
action: 'tigerstyle_heat_generate_amppackager_config',
_ajax_nonce: '<?php echo wp_create_nonce('tigerstyle_heat_nonce'); ?>'
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
// Create download link for config file
const blob = new Blob([data.data.config], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'amppackager-config.json';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
alert('Configuration file downloaded. Upload to your amppackager server.');
} else {
alert('Failed to generate config: ' + data.data.message);
}
})
.catch(error => {
alert('Generation failed: ' + error.message);
})
.finally(() => {
this.textContent = 'Generate amppackager Config';
this.disabled = false;
});
});
});
</script>