PluginProbe
SEO Engine – Smart SEO with AI, Schema & Redirection for WordPress / trunk
SEO Engine – Smart SEO with AI, Schema & Redirection for WordPress vtrunk
0.9.3 0.9.2 0.9.1 0.9.0 0.8.9 0.8.8 0.8.7 0.8.6 0.8.5 0.8.4 0.8.2 0.8.1 0.7.9 0.8.0 0.7.7 0.7.8 0.7.6 0.7.5 0.7.4 0.7.3 0.7.2 0.7.1 0.7.0 0.6.5 trunk All 87 releases
seo-engine / classes / admin.php

admin.php in SEO Engine – Smart SEO with AI, Schema & Redirection for WordPress trunk, at classes/admin.php

235 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 class Meow_MWSEO_Admin extends MeowKit_MWSEO_Admin {
3
4 public $core;
5
6 public function __construct( $core ) {
7 $this->core = $core;
8
9 parent::__construct( MWSEO_PREFIX, MWSEO_ENTRY, MWSEO_DOMAIN, class_exists( 'MeowPro_MWSEO_Core' ) );
10 if ( is_admin() ) {
11 add_action( 'admin_menu', array( $this, 'app_menu' ) );
12 // Move "Surgical SEO" to sit right under "All Posts" (the position arg is unreliable
13 // once other plugins add their own items, so we reorder the submenu explicitly).
14 add_action( 'admin_menu', array( $this, 'reorder_surgical_submenu' ), 999 );
15
16 // Load the scripts only if they are needed by the current screen
17 $page = isset( $_GET["page"] ) ? sanitize_text_field( $_GET["page"] ) : null;
18
19 $is_seo_engine_screen = in_array( $page, [ MWSEO_PREFIX . '_settings', 'seo_engine_dashboard' ] );
20 $is_meowapps_dashboard = $page === 'meowapps-main-menu';
21 $is_surgical_screen = in_array( $page, [ 'mwseo_surgical_post', 'mwseo_surgical_page' ], true );
22
23 if ( $is_meowapps_dashboard || $is_seo_engine_screen || $is_surgical_screen ) {
24 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
25 }
26 }
27
28 add_action( 'wp_head', array( $this, 'seo_engine_headers' ) );
29 add_action( 'wp_head', array( $this, 'seo_engine_sitemap_headers' ) );
30 }
31
32 function seo_engine_sitemap_headers() {
33
34 $disabled = $this->core->get_option( 'disable_wp_sitemap', false );
35 $custom = $this->core->get_option( 'sitemap_custom', false );
36
37 if( $disabled && !$custom ) {
38 return;
39 }
40
41 $excluded_posts = $this->core->get_option( 'sitemap_excluded_post_ids', [] );
42 $excluded_posts = array_map( 'intval', $excluded_posts );
43 $post_id = get_the_ID();
44
45 if( !in_array( $post_id, $excluded_posts ) ) {
46 return;
47 }
48
49 echo '<!-- SEO Engine: This post is excluded from the sitemap -->';
50 echo '<meta name="robots" content="noindex, follow">';
51 }
52
53 function seo_engine_headers() {
54 if( !$this->core->get_option( 'social_networks', false ) ) { return; }
55 // Step back when another SEO plugin is handling the frontend meta tags.
56 if( !$this->core->should_render_frontend_meta() ) { return; }
57
58 // use open graph tags for social networks, we should use the featured image, title and excerpt
59 if ( is_single() || is_page() ) {
60 $featured_image = apply_filters( 'mwseo_featured_image', get_the_post_thumbnail_url(), get_the_ID() );
61 $featured_image_alt = get_post_meta( get_post_thumbnail_id(), '_wp_attachment_image_alt', true );
62 $excerpt = get_the_excerpt();
63 $title = get_the_title();
64 $site_name = get_bloginfo('name');
65 $site_url = get_bloginfo('url');
66 $site_domain_name = parse_url($site_url, PHP_URL_HOST);
67 $site_description = get_bloginfo('description');
68 $site_icon = get_site_icon_url();
69
70 $site_twitter = $this->core->get_option('social_networks_twitter', null);
71 if ( !empty( $site_twitter ) ) {
72 $site_twitter = $site_twitter[0] === '@' ? $site_twitter : '@' . $site_twitter;
73 }
74
75 $site_facebook_app_id = $this->core->get_option('social_networks_facebook_app_id', null);
76
77 #region General Open Graph tags
78
79 echo '<meta property="og:site_name" content="' . esc_attr($site_name) . '">';
80 echo '<meta property="og:url" content="' . esc_url(get_permalink()) . '">';
81 echo '<meta property="og:title" content="' . esc_attr($title) . '">';
82 echo '<meta property="og:description" content="' . esc_attr($excerpt) . '">';
83 echo '<meta property="og:image" content="' . esc_url($featured_image) . '">';
84 echo '<meta property="og:image:alt" content="' . esc_attr($featured_image_alt) . '">';
85 // Homepage (whether "Latest posts" or a static page in Settings → Reading) is
86 // a "website" per OG spec; only single posts/pages get "article".
87 $og_type = is_front_page() ? 'website' : 'article';
88 echo '<meta property="og:type" content="' . esc_attr( $og_type ) . '">';
89 echo '<meta property="og:locale" content="' . esc_attr(get_locale()) . '">';
90 echo '<meta property="og:locale:alternate" content="' . esc_attr(get_locale()) . '">';
91 echo '<meta property="og:site" content="' . esc_url($site_url) . '">';
92 echo '<meta property="og:site_description" content="' . esc_attr($site_description) . '">';
93 echo '<meta property="og:site_icon" content="' . esc_url($site_icon) . '">';
94
95 if ( !empty( $site_twitter ) ) {
96 echo '<meta property="og:site_twitter" content="' . esc_attr($site_twitter) . '">';
97 }
98
99 #endregion
100
101 #region Twitter Open Graph tags
102 echo '<!-- Twitter Meta Tags -->';
103 echo '<meta name="twitter:card" content="summary_large_image">';
104 echo '<meta name="twitter:image" content="' . esc_url($featured_image) . '">';
105 echo '<meta name="twitter:image:alt" content="' . esc_attr($featured_image_alt) . '">';
106
107 echo '<meta name="twitter:title" content="' . esc_attr($title) . '">';
108 echo '<meta name="twitter:description" content="' . esc_attr($excerpt) . '">';
109
110 echo '<meta name="twitter:domain" content="' . esc_attr($site_domain_name) . '">';
111 echo '<meta name="twitter:url" content="' . esc_url(get_permalink()) . '">';
112
113 if ( !empty( $site_twitter ) ) {
114 echo '<meta name="twitter:site" content="' . esc_attr($site_twitter) . '">';
115 echo '<meta name="twitter:creator" content="' . esc_attr($site_twitter) . '">';
116 };
117 #endregion
118
119 #region Facebook Open Graph tags
120 if ( !empty( $site_facebook_app_id ) ) {
121 echo '<!-- Facebook Meta Tags -->';
122 echo '<meta property="fb:app_id" content="' . esc_attr($site_facebook_app_id) . '">';
123 }
124 #endregion
125
126 echo '<!-- Open Graph Meta Tags Powered With Love By SEO Engine 😽 -->';
127 }
128 }
129
130 function admin_enqueue_scripts() {
131
132 // Load the scripts
133 $physical_file = MWSEO_PATH . '/app/index.js';
134 $cache_buster = file_exists( $physical_file ) ? filemtime( $physical_file ) : MWSEO_VERSION;
135 wp_register_script( 'seo_engine_seo-vendor', MWSEO_URL . 'app/vendor.js',
136 ['wp-element', 'wp-i18n'], $cache_buster
137 );
138 wp_register_script( 'seo_engine_seo', MWSEO_URL . 'app/index.js',
139 ['seo_engine_seo-vendor', 'wp-i18n'], $cache_buster
140 );
141 wp_set_script_translations( 'seo_engine_seo', 'seo-engine' );
142 wp_enqueue_script('seo_engine_seo' );
143
144 // Localize and options
145 $js = [
146 'api_url' => rest_url( 'seo-engine/v1' ),
147 'rest_url' => rest_url(),
148 'plugin_url' => MWSEO_URL,
149 'prefix' => MWSEO_PREFIX,
150 'domain' => MWSEO_DOMAIN,
151 'is_pro' => class_exists( 'MeowPro_MWSEO_Core' ),
152 'is_registered' => !!$this->is_registered(),
153 'rest_nonce' => wp_create_nonce( 'wp_rest' ),
154 'fabicon_url' => get_site_icon_url(),
155 'site_name' => get_bloginfo('name'),
156 'options' => $this->core->sanitized_options(),
157 'google_analytics' => $this->core->get_google_analytics_state(),
158 'google_search_console' => $this->core->get_google_search_console_state(),
159 'blog_name' => trim( get_bloginfo( 'name' ) ),
160 'active_seo_plugins' => [
161 'yoast' => class_exists( 'WPSEO_Frontend' ),
162 'all_in_one' => class_exists( 'All_in_One_SEO_Pack' ),
163 'rank_math' => class_exists( 'RankMath' ),
164 'seopress' => class_exists( 'SEOPress' ),
165 ]
166 ];
167
168 wp_localize_script( 'seo_engine_seo', 'mwseo', $js );
169 }
170
171 function is_registered() {
172 return apply_filters( MWSEO_PREFIX . '_meowapps_is_registered', false, MWSEO_PREFIX );
173 }
174
175 function app_menu() {
176 add_submenu_page( 'meowapps-main-menu', 'SEO Engine', 'SEO Engine', 'manage_options',
177 MWSEO_PREFIX . '_settings', array( $this, 'admin_settings' ) );
178
179 // Standalone "Surgical SEO" posts manager — shown alongside (never replacing) the
180 // native post lists, only when the Content SEO module is enabled, per enabled post type.
181 if ( $this->core->get_option( 'content_seo', false ) ) {
182 $post_types = (array) $this->core->get_option( 'select_post_types', ['post', 'page'] );
183
184 if ( in_array( 'post', $post_types, true ) ) {
185 $label = __( 'All Posts (SEO)', 'seo-engine' );
186 add_submenu_page( 'edit.php', $label, $label, 'manage_options',
187 'mwseo_surgical_post', array( $this, 'render_surgical_posts' ) );
188 }
189 if ( in_array( 'page', $post_types, true ) ) {
190 $label = __( 'All Pages (SEO)', 'seo-engine' );
191 add_submenu_page( 'edit.php?post_type=page', $label, $label, 'manage_options',
192 'mwseo_surgical_page', array( $this, 'render_surgical_page' ) );
193 }
194 }
195 }
196
197 // Reorders the Posts/Pages submenus so "Surgical SEO" appears right after "All Posts".
198 function reorder_surgical_submenu() {
199 global $submenu;
200 $targets = [
201 'edit.php' => 'mwseo_surgical_post',
202 'edit.php?post_type=page' => 'mwseo_surgical_page',
203 ];
204 foreach ( $targets as $parent => $slug ) {
205 if ( empty( $submenu[ $parent ] ) ) continue;
206 $found = null;
207 foreach ( $submenu[ $parent ] as $i => $item ) {
208 if ( isset( $item[2] ) && $item[2] === $slug ) { $found = $i; break; }
209 }
210 if ( $found === null ) continue;
211 $entry = $submenu[ $parent ][ $found ];
212 unset( $submenu[ $parent ][ $found ] );
213 $items = array_values( $submenu[ $parent ] );
214 // Insert just after the first entry ("All Posts" / "All Pages").
215 array_splice( $items, 1, 0, [ $entry ] );
216 $submenu[ $parent ] = $items;
217 }
218 }
219
220 function admin_settings() {
221 echo '<div id="' . MWSEO_PREFIX . '-admin-settings"></div>';
222 }
223
224 function render_surgical_posts() {
225 echo '<div id="mwseo-surgical-app" data-post-type="post"></div>';
226 }
227
228 function render_surgical_page() {
229 echo '<div id="mwseo-surgical-app" data-post-type="page"></div>';
230 }
231
232
233 }
234
235 ?>