-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwp-custom-schema-override.php
More file actions
151 lines (127 loc) · 4.04 KB
/
Copy pathwp-custom-schema-override.php
File metadata and controls
151 lines (127 loc) · 4.04 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php
/**
* Plugin Name: WP Custom Schema Override
* Description: Adds a per-page JSON-LD field that, when populated, replaces all Yoast SEO structured data output with the custom value.
* Version: 1.0.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( ! defined( 'WPCSO_META_KEY' ) ) {
define( 'WPCSO_META_KEY', '_custom_schema_json_override' );
}
if ( ! defined( 'WPCSO_NONCE_KEY' ) ) {
define( 'WPCSO_NONCE_KEY', 'wpcso_nonce' );
}
// ---------------------------------------------------------------------------
// Admin: meta box
// ---------------------------------------------------------------------------
add_action( 'add_meta_boxes', 'wpcso_add_meta_box' );
function wpcso_add_meta_box(): void {
$post_types = get_post_types( [ 'public' => true ] );
foreach ( $post_types as $post_type ) {
add_meta_box(
'wpcso_schema_override',
'Custom Schema JSON-LD Override',
'wpcso_render_meta_box',
$post_type,
'side',
'default'
);
}
}
function wpcso_render_meta_box( WP_Post $post ): void {
$value = get_post_meta( $post->ID, WPCSO_META_KEY, true );
wp_nonce_field( 'wpcso_save', WPCSO_NONCE_KEY );
?>
<p style="margin-bottom:6px;color:#555;">
Enter raw JSON-LD to replace <strong>all</strong> Yoast SEO structured data on this page.
Must be a valid JSON object or array. Leave empty to use the default Yoast output.
</p>
<textarea
id="wpcso_schema_json"
name="wpcso_schema_json"
rows="14"
style="width:100%;font-family:monospace;font-size:0.85em;"
><?php echo esc_textarea( $value ); ?></textarea>
<p id="wpcso_json_error" style="color:#c00;display:none;margin-top:4px;"></p>
<script>
(function () {
var textarea = document.getElementById('wpcso_schema_json');
var error = document.getElementById('wpcso_json_error');
textarea.addEventListener('blur', function () {
var val = textarea.value.trim();
if ( ! val ) { error.style.display = 'none'; return; }
try {
JSON.parse(val);
error.style.display = 'none';
} catch (e) {
error.textContent = 'Invalid JSON: ' + e.message;
error.style.display = 'block';
}
});
}());
</script>
<?php
}
add_action( 'save_post', 'wpcso_save_meta' );
function wpcso_save_meta( int $post_id ): void {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}
if ( wp_is_post_revision( $post_id ) ) {
return;
}
if ( ! isset( $_POST[ WPCSO_NONCE_KEY ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ WPCSO_NONCE_KEY ] ) ), 'wpcso_save' ) ) {
return;
}
if ( ! current_user_can( 'unfiltered_html' ) ) {
return;
}
if ( ! isset( $_POST['wpcso_schema_json'] ) ) {
return;
}
$raw = trim( wp_unslash( $_POST['wpcso_schema_json'] ) );
if ( $raw === '' ) {
delete_post_meta( $post_id, WPCSO_META_KEY );
return;
}
// Reject invalid JSON server-side.
json_decode( $raw );
if ( json_last_error() !== JSON_ERROR_NONE ) {
return;
}
update_post_meta( $post_id, WPCSO_META_KEY, $raw );
}
// ---------------------------------------------------------------------------
// Front end: replace Yoast schema when the override is set
// ---------------------------------------------------------------------------
add_action( 'wp', 'wpcso_maybe_hook_frontend' );
function wpcso_maybe_hook_frontend(): void {
if ( ! is_singular() ) {
return;
}
$post_id = get_queried_object_id();
$raw = get_post_meta( $post_id, WPCSO_META_KEY, true );
if ( empty( $raw ) ) {
return;
}
$decoded = json_decode( $raw );
if ( $decoded === null ) {
return;
}
// Encode with the same safety flags used in the Yoast.
$safe_json = wp_json_encode(
$decoded,
JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE
);
if ( $safe_json === false ) {
return;
}
// Disable Yoast's schema output entirely.
add_filter( 'wpseo_json_ld_output', '__return_false' );
// Output our custom JSON-LD in its place.
add_action( 'wp_head', static function () use ( $safe_json ): void {
echo '<script type="application/ld+json">' . "\n" . $safe_json . "\n" . '</script>' . "\n";
}, 90 );
}