| @@ -178,9 +178,9 @@ | ||
| 178 | 178 | if ($value === '') { |
| 179 | 179 | return false; |
| 180 | 180 | } |
| 181 | 181 | # 'default' text domain, matching the __() call core itself uses. |
| 182 | - if ($value === 'Auto Draft' || $value === trim(__('Auto Draft'))) { | |
| 182 | + if ($value === 'Auto Draft' || $value === trim(__('Auto Draft', 'default'))) { // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- resolves core's own 'Auto Draft' placeholder, which only exists in core's 'default' domain; the plugin text domain cannot reproduce it | |
| 183 | 183 | return true; |
| 184 | 184 | } |
| 185 | 185 | # Registry arm: a variant captured in another locale's admin request. |
| 186 | 186 | # Without it the guard above is English-only on every non-admin request, |
| @@ -241,9 +241,9 @@ | ||
| 241 | 241 | * |
| 242 | 242 | * @return void |
| 243 | 243 | */ |
| 244 | 244 | public static function seed_auto_draft_placeholder() { |
| 245 | - self::remember_auto_draft_placeholder(__('Auto Draft')); | |
| 245 | + self::remember_auto_draft_placeholder(__('Auto Draft', 'default')); // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- resolves core's own 'Auto Draft' placeholder, which only exists in core's 'default' domain; the plugin text domain cannot reproduce it | |
| 246 | 246 | } |
| 247 | 247 | |
| 248 | 248 | /** |
| 249 | 249 | * Read a persisted social meta value, collapsing the "Auto Draft" placeholder |
| @@ -730,9 +730,9 @@ | ||
| 730 | 730 | |
| 731 | 731 | # LPS / custom-HTML pages bake their own OG/social tags into their HTML bundle, |
| 732 | 732 | # served before wp_head — so this box does nothing on them. Hide it; the SEO |
| 733 | 733 | # read-only notice covers the messaging. |
| 734 | - $lps_post_id = isset($_GET['post']) ? intval($_GET['post']) : (isset($_POST['post_ID']) ? intval($_POST['post_ID']) : 0); | |
| 734 | + $lps_post_id = isset($_GET['post']) ? intval($_GET['post']) : (isset($_POST['post_ID']) ? intval($_POST['post_ID']) : 0); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.NonceVerification.Recommended -- read-only post ID to pick meta boxes | |
| 735 | 735 | if (function_exists('metasync_is_custom_or_lps_page') && $lps_post_id > 0 && metasync_is_custom_or_lps_page($lps_post_id)) { |
| 736 | 736 | return; |
| 737 | 737 | } |
| 738 | 738 | |
| @@ -742,8 +742,9 @@ | ||
| 742 | 742 | |
| 743 | 743 | foreach ($post_types as $post_type) { |
| 744 | 744 | add_meta_box( |
| 745 | 745 | self::META_BOX_ID, |
| 746 | + /* translators: %s: effective plugin name (whitelabel-aware). */ | |
| 746 | 747 | sprintf(esc_html__('Social Media & Open Graph by %s', 'metasync'), $plugin_name), |
| 747 | 748 | [$this, 'render_meta_box'], |
| 748 | 749 | $post_type, |
| 749 | 750 | 'normal', |
| @@ -1044,9 +1045,9 @@ | ||
| 1044 | 1045 | # remember_auto_draft_placeholder() already skips. Verified against |
| 1045 | 1046 | # __( 'Auto Draft' ), never trusted as a bare string, so a builder's |
| 1046 | 1047 | # custom-seeded auto-draft title is never captured. |
| 1047 | 1048 | if ($post->post_status === 'auto-draft' |
| 1048 | - && $title !== '' && $title === trim(__('Auto Draft'))) { | |
| 1049 | + && $title !== '' && $title === trim(__('Auto Draft', 'default'))) { // phpcs:ignore WordPress.WP.I18n.TextDomainMismatch -- resolves core's own 'Auto Draft' placeholder, which only exists in core's 'default' domain; the plugin text domain cannot reproduce it | |
| 1049 | 1050 | self::remember_auto_draft_placeholder($title); |
| 1050 | 1051 | } |
| 1051 | 1052 | |
| 1052 | 1053 | # Flag maintenance: recognition is context-independent thanks to the |
| @@ -1785,9 +1786,9 @@ | ||
| 1785 | 1786 | private function generate_preview_html($title, $description, $image, $url, $twitter_title = '', $twitter_description = '', $twitter_image = '') { |
| 1786 | 1787 | # Parse domain from URL |
| 1787 | 1788 | $domain = ''; |
| 1788 | 1789 | if (!empty($url)) { |
| 1789 | - $parsed = parse_url($url); | |
| 1790 | + $parsed = wp_parse_url($url); | |
| 1790 | 1791 | $domain = $parsed['host'] ?? ''; |
| 1791 | 1792 | } |
| 1792 | 1793 | |
| 1793 | 1794 | # Fallback to site URL if no domain found |
| @@ -1792,9 +1793,9 @@ | ||
| 1792 | 1793 | |
| 1793 | 1794 | # Fallback to site URL if no domain found |
| 1794 | 1795 | if (empty($domain)) { |
| 1795 | 1796 | $site_url = get_site_url(); |
| 1796 | - $parsed = parse_url($site_url); | |
| 1797 | + $parsed = wp_parse_url($site_url); | |
| 1797 | 1798 | $domain = $parsed['host'] ?? 'your-site.com'; |
| 1798 | 1799 | } |
| 1799 | 1800 | |
| 1800 | 1801 | # Provide fallbacks for empty values |
| @@ -2371,11 +2372,20 @@ | ||
| 2371 | 2372 | /** |
| 2372 | 2373 | * Check if post slug changed via edit slug functionality |
| 2373 | 2374 | */ |
| 2374 | 2375 | public function check_slug_change() { |
| 2376 | + # Fires on the same admin-ajax request as core's sample-permalink | |
| 2377 | + # handler, but at priority 5 — before core validates the nonce and | |
| 2378 | + # capability at its own priority-10 callback. Verify them ourselves | |
| 2379 | + # before any post-meta write. Return (not die) so core still serves | |
| 2380 | + # the preview request normally. | |
| 2381 | + if (!check_ajax_referer('samplepermalink', 'samplepermalinknonce', false)) { | |
| 2382 | + return; | |
| 2383 | + } | |
| 2384 | + | |
| 2375 | 2385 | # Get the post ID from the request |
| 2376 | 2386 | $post_id = isset($_POST['post_id']) ? intval($_POST['post_id']) : 0; |
| 2377 | - if (!$post_id) { | |
| 2387 | + if (!$post_id || !current_user_can('edit_post', $post_id)) { | |
| 2378 | 2388 | return; |
| 2379 | 2389 | } |
| 2380 | 2390 | |
| 2381 | 2391 | # Only check for supported post types |
| @@ -2408,9 +2418,9 @@ | ||
| 2408 | 2418 | # This means the og:url was set to the post permalink (not a custom URL) |
| 2409 | 2419 | if ($current_og_url !== $current_permalink && strpos($current_og_url, '?p=') === false) { |
| 2410 | 2420 | # Check if the og:url was the old permalink by comparing with a generated old permalink |
| 2411 | 2421 | $old_post = clone $post; |
| 2412 | - $old_slug = isset($_POST['new_slug']) ? sanitize_title($_POST['new_slug']) : $post->post_name; | |
| 2422 | + $old_slug = isset($_POST['new_slug']) ? sanitize_title(wp_unslash($_POST['new_slug'])) : $post->post_name; | |
| 2413 | 2423 | |
| 2414 | 2424 | # If the og:url doesn't match the current permalink, it might be the old one |
| 2415 | 2425 | # We'll update it to the new permalink |
| 2416 | 2426 | if ($current_og_url !== $current_permalink) { |