wp-seopress
Last commit date
assets
7 months ago
inc
7 months ago
languages
7 months ago
public
7 months ago
src
7 months ago
templates
7 months ago
vendor
7 months ago
contributors.txt
7 months ago
readme.txt
7 months ago
seopress-autoload.php
7 months ago
seopress-functions.php
7 months ago
seopress.php
7 months ago
uninstall.php
7 months ago
wpml-config.xml
7 months ago
seopress-functions.php
821 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * Functions file |
| 5 | * |
| 6 | * @package SEOPress |
| 7 | */ |
| 8 | |
| 9 | defined( 'ABSPATH' ) || exit( 'Please don’t call the plugin directly. Thanks :)' ); |
| 10 | |
| 11 | use SEOPress\Core\Kernel; |
| 12 | |
| 13 | /** |
| 14 | * Get a service. |
| 15 | * |
| 16 | * @param string $service |
| 17 | * |
| 18 | * @return object |
| 19 | */ |
| 20 | function seopress_get_service( $service ) { |
| 21 | return Kernel::getContainer()->getServiceByName( $service ); |
| 22 | } |
| 23 | |
| 24 | if ( ! function_exists( 'array_key_last' ) ) { |
| 25 | /** |
| 26 | * Get last key of an array if PHP < 7.3 |
| 27 | * |
| 28 | * @param array $arr |
| 29 | * |
| 30 | * @return string |
| 31 | */ |
| 32 | function array_key_last( array $arr ) { |
| 33 | end( $arr ); |
| 34 | $key = key( $arr ); |
| 35 | |
| 36 | return $key; |
| 37 | } |
| 38 | } |
| 39 | |
| 40 | if ( ! function_exists( 'array_key_first' ) ) { |
| 41 | /** |
| 42 | * Get first key of an array if PHP < 7.3 |
| 43 | * |
| 44 | * @param array $arr |
| 45 | * |
| 46 | * @return string |
| 47 | */ |
| 48 | function array_key_first( array $arr ) { |
| 49 | foreach ( $arr as $key => $unused ) { |
| 50 | return $key; |
| 51 | } |
| 52 | |
| 53 | return null; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | /** |
| 58 | * Remove default WordPress Canonical |
| 59 | */ |
| 60 | remove_action( 'wp_head', 'rel_canonical' ); |
| 61 | |
| 62 | /** |
| 63 | * Remove WP default meta robots (added in WP 5.7) |
| 64 | */ |
| 65 | remove_filter( 'wp_robots', 'wp_robots_max_image_preview_large' ); |
| 66 | |
| 67 | /** |
| 68 | * Remove WC default meta robots (added in WP 5.7) |
| 69 | * |
| 70 | * @param array $robots |
| 71 | * |
| 72 | * @todo use wp_robots API |
| 73 | */ |
| 74 | function seopress_robots_wc_pages( $robots ) { |
| 75 | include_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 76 | if ( is_plugin_active( 'woocommerce/woocommerce.php' ) ) { |
| 77 | if ( function_exists( 'wc_get_page_id' ) ) { |
| 78 | if ( is_page( wc_get_page_id( 'cart' ) ) || is_page( wc_get_page_id( 'checkout' ) ) || is_page( wc_get_page_id( 'myaccount' ) ) ) { |
| 79 | if ( '0' === get_option( 'blog_public' ) ) { |
| 80 | return $robots; |
| 81 | } else { |
| 82 | unset( $robots ); |
| 83 | $robots = array(); |
| 84 | |
| 85 | return $robots; |
| 86 | } |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | // Remove noindex on search archive pages. |
| 91 | if ( is_search() ) { |
| 92 | if ( '0' === get_option( 'blog_public' ) ) { |
| 93 | return $robots; |
| 94 | } else { |
| 95 | unset( $robots ); |
| 96 | $robots = array(); |
| 97 | |
| 98 | return $robots; |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | return $robots; |
| 103 | } |
| 104 | add_filter( 'wp_robots', 'seopress_robots_wc_pages', 20 ); |
| 105 | |
| 106 | /** |
| 107 | * Remove default WC meta robots (useful for WooCommerce < 5.7). |
| 108 | */ |
| 109 | function seopress_compatibility_woocommerce() { |
| 110 | if ( ! is_admin() && function_exists( 'is_plugin_active' ) && is_plugin_active( 'woocommerce/woocommerce.php' ) ) { |
| 111 | remove_action( 'wp_head', 'wc_page_noindex' ); |
| 112 | } |
| 113 | } |
| 114 | add_action( 'wp_head', 'seopress_compatibility_woocommerce', 0 ); |
| 115 | |
| 116 | /** |
| 117 | * Remove Elementor description meta tag. |
| 118 | */ |
| 119 | function seopress_compatibility_hello_elementor() { |
| 120 | remove_action( 'wp_head', 'hello_elementor_add_description_meta_tag' ); |
| 121 | } |
| 122 | add_action( 'after_setup_theme', 'seopress_compatibility_hello_elementor' ); |
| 123 | |
| 124 | if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'sg-cachepress/sg-cachepress.php' ) ) { |
| 125 | /** |
| 126 | * Filter the xml sitemap URL used by SiteGround Optimizer for preheating. |
| 127 | * |
| 128 | * @param string $url URL to be preheated. |
| 129 | */ |
| 130 | function sp_sg_file_caching_preheat_xml( $url ) { |
| 131 | $url = get_home_url() . '/sitemaps.xml'; |
| 132 | |
| 133 | return $url; |
| 134 | } |
| 135 | add_filter( 'sg_file_caching_preheat_xml', 'sp_sg_file_caching_preheat_xml' ); |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Remove WPML home url filter. |
| 140 | * |
| 141 | * @param mixed $home_url |
| 142 | * @param mixed $url |
| 143 | * @param mixed $path |
| 144 | * @param string $orig_scheme |
| 145 | * @param int $blog_id |
| 146 | */ |
| 147 | function seopress_remove_wpml_home_url_filter( $home_url, $url, $path, $orig_scheme, $blog_id ) { |
| 148 | return $url; |
| 149 | } |
| 150 | |
| 151 | /** |
| 152 | * Remove third-parties metaboxes on our CPT |
| 153 | */ |
| 154 | add_action( 'do_meta_boxes', 'seopress_remove_metaboxes', 10 ); |
| 155 | function seopress_remove_metaboxes() { |
| 156 | // Oxygen Builder. |
| 157 | remove_meta_box( 'ct_views_cpt', 'seopress_404', 'normal' ); |
| 158 | remove_meta_box( 'ct_views_cpt', 'seopress_schemas', 'normal' ); |
| 159 | remove_meta_box( 'ct_views_cpt', 'seopress_bot', 'normal' ); |
| 160 | } |
| 161 | |
| 162 | /** |
| 163 | * Get all custom fields (limit: 250). |
| 164 | * |
| 165 | * @return array custom field keys |
| 166 | */ |
| 167 | function seopress_get_custom_fields() { |
| 168 | $cf_keys = wp_cache_get( 'seopress_get_custom_fields' ); |
| 169 | |
| 170 | if ( false === $cf_keys ) { |
| 171 | global $wpdb; |
| 172 | |
| 173 | $limit = (int) apply_filters( 'postmeta_form_limit', 250 ); |
| 174 | $cf_keys = $wpdb->get_col( |
| 175 | $wpdb->prepare( |
| 176 | " |
| 177 | SELECT DISTINCT meta_key |
| 178 | FROM $wpdb->postmeta |
| 179 | GROUP BY meta_key |
| 180 | HAVING meta_key NOT LIKE '\_%%' |
| 181 | ORDER BY meta_key |
| 182 | LIMIT %d", |
| 183 | $limit |
| 184 | ) |
| 185 | ); |
| 186 | |
| 187 | if ( is_plugin_active( 'types/wpcf.php' ) ) { |
| 188 | $wpcf_fields = get_option( 'wpcf-fields' ); |
| 189 | |
| 190 | if ( ! empty( $wpcf_fields ) ) { |
| 191 | foreach ( $wpcf_fields as $key => $value ) { |
| 192 | $cf_keys[] = $value['meta_key']; |
| 193 | } |
| 194 | } |
| 195 | } |
| 196 | |
| 197 | $cf_keys = apply_filters( 'seopress_get_custom_fields', $cf_keys ); |
| 198 | |
| 199 | if ( $cf_keys ) { |
| 200 | natcasesort( $cf_keys ); |
| 201 | } |
| 202 | wp_cache_set( 'seopress_get_custom_fields', $cf_keys ); |
| 203 | } |
| 204 | |
| 205 | return $cf_keys; |
| 206 | } |
| 207 | |
| 208 | /** |
| 209 | * Check SSL for schema.org. |
| 210 | * |
| 211 | * @return string correct protocol |
| 212 | */ |
| 213 | function seopress_check_ssl() { |
| 214 | if ( is_ssl() ) { |
| 215 | return 'https://'; |
| 216 | } else { |
| 217 | return 'http://'; |
| 218 | } |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * Check if a string is base64 encoded |
| 223 | * |
| 224 | * @param string $str The string to check |
| 225 | * @return bool Returns true if the string is base64 encoded, false otherwise |
| 226 | */ |
| 227 | function seopress_is_base64_string( $str ) { |
| 228 | // Check if the string is empty or not a string. |
| 229 | if ( empty( $str ) || ! is_string( $str ) ) { |
| 230 | return false; |
| 231 | } |
| 232 | |
| 233 | // Decode the string and check if it decodes properly. |
| 234 | $decoded = base64_decode( $str, true ); |
| 235 | if ( $decoded === false ) { |
| 236 | return false; |
| 237 | } |
| 238 | |
| 239 | // Encode the decoded string and compare with the original string. |
| 240 | return base64_encode( $decoded ) === $str; |
| 241 | } |
| 242 | |
| 243 | /** |
| 244 | * Disable Query Monitor for CA. |
| 245 | * |
| 246 | * @return array |
| 247 | * |
| 248 | * @param mixed $url |
| 249 | * @param mixed $allcaps |
| 250 | * @param mixed $caps |
| 251 | * @param mixed $args |
| 252 | */ |
| 253 | function seopress_disable_qm( $allcaps, $caps, $args ) { |
| 254 | $allcaps['view_query_monitor'] = false; |
| 255 | |
| 256 | return $allcaps; |
| 257 | } |
| 258 | /** |
| 259 | * Clear content for CA. |
| 260 | */ |
| 261 | function seopress_clean_content_analysis() { |
| 262 | // Check if 'no_admin_bar' is set and equals '1'; sanitize input. |
| 263 | if ( ! isset( $_GET['no_admin_bar'] ) || '1' !== sanitize_text_field( wp_unslash( $_GET['no_admin_bar'] ) ) ) { |
| 264 | return; |
| 265 | } |
| 266 | |
| 267 | // Check if the user is logged in and has the necessary capability. |
| 268 | if ( ! is_user_logged_in() || ! current_user_can( 'edit_posts' ) ) { |
| 269 | return; |
| 270 | } |
| 271 | |
| 272 | // Remove admin bar. |
| 273 | add_filter( 'show_admin_bar', '__return_false' ); |
| 274 | |
| 275 | // Disable Query Monitor. |
| 276 | add_filter( 'user_has_cap', 'seopress_disable_qm', 10, 3 ); |
| 277 | |
| 278 | // Disable wptexturize. |
| 279 | add_filter( 'run_wptexturize', '__return_false' ); |
| 280 | |
| 281 | // Remove Edit nofollow links from TablePress. |
| 282 | add_filter( 'tablepress_edit_link_below_table', '__return_false' ); |
| 283 | |
| 284 | // Allow user to run custom action to clean content. |
| 285 | do_action( 'seopress_content_analysis_cleaning' ); |
| 286 | } |
| 287 | add_action( 'plugins_loaded', 'seopress_clean_content_analysis' ); |
| 288 | |
| 289 | /** |
| 290 | * Test if a URL is in absolute. |
| 291 | * |
| 292 | * @return bool true if absolute |
| 293 | * |
| 294 | * @param mixed $url |
| 295 | */ |
| 296 | function seopress_is_absolute( $url ) { |
| 297 | $pattern = "%^(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@|\d{1,3}(?:\.\d{1,3}){3}|(?:(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)(?:\.(?:[a-z\d\x{00a1}-\x{ffff}]+-?)*[a-z\d\x{00a1}-\x{ffff}]+)*(?:\.[a-z\x{00a1}-\x{ffff}]{2,6}))(?::\d+)?(?:[^\s]*)?$%iu"; |
| 298 | |
| 299 | return (bool) preg_match( $pattern, $url ); |
| 300 | } |
| 301 | |
| 302 | /** |
| 303 | * Manage localized links. |
| 304 | * |
| 305 | * @return string locale for documentation links |
| 306 | */ |
| 307 | function seopress_get_locale() { |
| 308 | switch ( get_user_locale( get_current_user_id() ) ) { |
| 309 | case 'fr_FR': |
| 310 | case 'fr_BE': |
| 311 | case 'fr_CA': |
| 312 | case 'fr_LU': |
| 313 | case 'fr_MC': |
| 314 | case 'fr_CH': |
| 315 | $locale_link = 'fr'; |
| 316 | break; |
| 317 | default: |
| 318 | $locale_link = ''; |
| 319 | break; |
| 320 | } |
| 321 | |
| 322 | return $locale_link; |
| 323 | } |
| 324 | |
| 325 | /** |
| 326 | * Extract correct locale in ISO format from get_locale(). |
| 327 | * |
| 328 | * @return string locale |
| 329 | */ |
| 330 | function seopress_normalized_locale( $current_locale ) { |
| 331 | if ( ! function_exists( 'locale_get_primary_language' ) ) { |
| 332 | return $current_locale; |
| 333 | } |
| 334 | |
| 335 | // Extract primary language and region. |
| 336 | $primary_language = locale_get_primary_language( $current_locale ); |
| 337 | $region = locale_get_region( $current_locale ); |
| 338 | |
| 339 | // Check if region is available, if not, return only the primary language. |
| 340 | $normalized_locale = $primary_language . ( $region ? '_' . $region : '' ); |
| 341 | |
| 342 | return $normalized_locale; |
| 343 | } |
| 344 | |
| 345 | /** |
| 346 | * Returns the language code by supporting multilingual plugins |
| 347 | * |
| 348 | * @return string language code |
| 349 | */ |
| 350 | function seopress_get_current_lang() { |
| 351 | // Default. |
| 352 | $lang = seopress_normalized_locale( get_locale() ); |
| 353 | |
| 354 | // Polylang. |
| 355 | if ( function_exists( 'pll_current_language' ) ) { |
| 356 | $lang = pll_current_language( 'locale' ); |
| 357 | } |
| 358 | |
| 359 | // WPML. |
| 360 | if ( defined( 'ICL_SITEPRESS_VERSION' ) ) { |
| 361 | $lang = apply_filters( 'wpml_current_language', null ); |
| 362 | } |
| 363 | |
| 364 | return $lang; |
| 365 | } |
| 366 | |
| 367 | /** |
| 368 | * Check empty global title template. |
| 369 | * |
| 370 | * @param string $type |
| 371 | * @param string $metadata |
| 372 | * @param bool $notice |
| 373 | * |
| 374 | * @return string notice with list of empty cpt titles |
| 375 | */ |
| 376 | function seopress_get_empty_templates( $type, $metadata, $notice = true ) { |
| 377 | $cpt_titles_empty = array(); |
| 378 | $templates = ''; |
| 379 | $data = ''; |
| 380 | $html = ''; |
| 381 | $list = ''; |
| 382 | |
| 383 | if ( 'cpt' === $type ) { |
| 384 | $templates = $postTypes = seopress_get_service( 'WordPressData' )->getPostTypes(); |
| 385 | $notice_i18n = __( 'Custom Post Types', 'wp-seopress' ); |
| 386 | } |
| 387 | if ( 'tax' === $type ) { |
| 388 | $templates = seopress_get_service( 'WordPressData' )->getTaxonomies(); |
| 389 | $notice_i18n = __( 'Custom Taxonomies', 'wp-seopress' ); |
| 390 | } |
| 391 | foreach ( $templates as $key => $value ) { |
| 392 | $options = get_option( 'seopress_titles_option_name' ); |
| 393 | |
| 394 | if ( ! empty( $options ) ) { |
| 395 | if ( 'cpt' === $type ) { |
| 396 | if ( ! empty( $options['seopress_titles_single_titles'] ) ) { |
| 397 | if ( ! array_key_exists( $key, $options['seopress_titles_single_titles'] ) ) { |
| 398 | $cpt_titles_empty[] = $key; |
| 399 | } else { |
| 400 | $data = isset( $options['seopress_titles_single_titles'][ $key ][ $metadata ] ) ? $options['seopress_titles_single_titles'][ $key ][ $metadata ] : ''; |
| 401 | } |
| 402 | } |
| 403 | } |
| 404 | if ( 'tax' === $type ) { |
| 405 | if ( ! empty( $options['seopress_titles_tax_titles'] ) ) { |
| 406 | if ( ! array_key_exists( $key, $options['seopress_titles_tax_titles'] ) ) { |
| 407 | $cpt_titles_empty[] = $key; |
| 408 | } else { |
| 409 | $data = isset( $options['seopress_titles_tax_titles'][ $key ][ $metadata ] ) ? $options['seopress_titles_tax_titles'][ $key ][ $metadata ] : ''; |
| 410 | } |
| 411 | } |
| 412 | } |
| 413 | } |
| 414 | |
| 415 | if ( empty( $data ) ) { |
| 416 | if ( seopress_get_service( 'TitleOption' )->getSingleCptEnable( $key ) !== '1' && seopress_get_service( 'TitleOption' )->getTaxEnable( $key ) !== '1' ) { |
| 417 | $cpt_titles_empty[] = $key; |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | if ( ! empty( $cpt_titles_empty ) ) { |
| 423 | $list .= '<ul>'; |
| 424 | foreach ( $cpt_titles_empty as $cpt ) { |
| 425 | $list .= '<li>' . $cpt . '</li>'; |
| 426 | } |
| 427 | $list .= '</ul>'; |
| 428 | |
| 429 | if ( false === $notice ) { |
| 430 | return $list; |
| 431 | } else { |
| 432 | $html .= '<div class="seopress-notice is-warning"> |
| 433 | <p>'; |
| 434 | /* translators: %1$s: "Custom Post Types" or "Custom Taxonomies", %2$s: "title" or "description" */ |
| 435 | $html .= wp_kses_post( sprintf( __( 'Some <strong>%1$s</strong> have no <strong>meta %2$s</strong> set! We strongly encourage you to add one by filling in the fields below.', 'wp-seopress' ), esc_attr( $notice_i18n ), esc_attr( $metadata ) ) ); |
| 436 | $html .= '</p>'; |
| 437 | $html .= $list; |
| 438 | $html .= '</div>'; |
| 439 | |
| 440 | return $html; |
| 441 | } |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | /** |
| 446 | * Generate Permalink notice to prevent users from changing the permastructure on a live site. |
| 447 | * |
| 448 | * @return void |
| 449 | */ |
| 450 | function seopress_notice_permalinks() { |
| 451 | $pagenow = isset( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : ''; |
| 452 | |
| 453 | if ( 'options-permalink.php' !== $pagenow ) { |
| 454 | return; |
| 455 | } |
| 456 | |
| 457 | $class = 'notice notice-warning'; |
| 458 | $message = sprintf( |
| 459 | '<p><strong>%s</strong></p><p>%s</p>', |
| 460 | __( 'WARNING', 'wp-seopress' ), |
| 461 | __( 'Do NOT change your permalink structure on a production site. Changing URLs can severely damage your SEO.', 'wp-seopress' ) |
| 462 | ); |
| 463 | |
| 464 | printf( '<div class="%1$s">%2$s</div>', esc_attr( $class ), wp_kses_post( $message ) ); |
| 465 | } |
| 466 | add_action( 'admin_notices', 'seopress_notice_permalinks' ); |
| 467 | |
| 468 | /** |
| 469 | * Generate a notice on permalink settings screen if URL rewriting is disabled. |
| 470 | * |
| 471 | * @return void |
| 472 | */ |
| 473 | function seopress_notice_no_rewrite_url() { |
| 474 | $pagenow = isset( $GLOBALS['pagenow'] ) ? $GLOBALS['pagenow'] : ''; |
| 475 | |
| 476 | // Check we are on the Permalink settings page. |
| 477 | if ( 'options-permalink.php' !== $pagenow ) { |
| 478 | return; |
| 479 | } |
| 480 | |
| 481 | // Check permalink structure. |
| 482 | if ( '' !== get_option( 'permalink_structure' ) ) { |
| 483 | return; |
| 484 | } |
| 485 | |
| 486 | // Display the notice. |
| 487 | $class = 'notice notice-warning'; |
| 488 | $message = sprintf( |
| 489 | '<p><strong>%s</strong></p><p>%s</p>', |
| 490 | __( 'WARNING', 'wp-seopress' ), |
| 491 | __( 'URL rewriting is NOT enabled on your site. Select a permalink structure that is optimized for SEO (NOT Plain).', 'wp-seopress' ) |
| 492 | ); |
| 493 | |
| 494 | printf( '<div class="%1$s">%2$s</div>', esc_attr( $class ), wp_kses_post( $message ) ); |
| 495 | } |
| 496 | add_action( 'admin_notices', 'seopress_notice_no_rewrite_url' ); |
| 497 | |
| 498 | /** |
| 499 | * Generate Tooltip. |
| 500 | * |
| 501 | * @param string $tooltip_title, $tooltip_desc, $tooltip_code |
| 502 | * @param mixed $tooltip_desc |
| 503 | * @param mixed $tooltip_code |
| 504 | * |
| 505 | * @return string tooltip title, tooltip description, tooltip url |
| 506 | */ |
| 507 | function seopress_tooltip( $tooltip_title, $tooltip_desc, $tooltip_code ) { |
| 508 | $html = |
| 509 | '<button type="button" class="sp-tooltip"><span class="dashicons dashicons-editor-help"></span> |
| 510 | <span class="sp-tooltiptext" role="tooltip" tabindex="0"> |
| 511 | <span class="sp-tooltip-headings">' . $tooltip_title . '</span> |
| 512 | <span class="sp-tooltip-desc">' . $tooltip_desc . '</span> |
| 513 | <span class="sp-tooltip-code">' . $tooltip_code . '</span> |
| 514 | </span></button>'; |
| 515 | |
| 516 | return $html; |
| 517 | } |
| 518 | |
| 519 | /** |
| 520 | * Generate Tooltip (alternative version). |
| 521 | * |
| 522 | * @param string $tooltip_title, $tooltip_desc, $tooltip_code |
| 523 | * @param mixed $tooltip_anchor |
| 524 | * @param mixed $tooltip_desc |
| 525 | * |
| 526 | * @return string tooltip title, tooltip description, tooltip url |
| 527 | */ |
| 528 | function seopress_tooltip_alt( $tooltip_anchor, $tooltip_desc ) { |
| 529 | $html = |
| 530 | '<button type="button" class="sp-tooltip alt">' . $tooltip_anchor . ' |
| 531 | <span class="sp-tooltiptext" role="tooltip" tabindex="0"> |
| 532 | <span class="sp-tooltip-desc">' . $tooltip_desc . '</span> |
| 533 | </span> |
| 534 | </button>'; |
| 535 | |
| 536 | return $html; |
| 537 | } |
| 538 | |
| 539 | /** |
| 540 | * Generate Tooltip link. |
| 541 | * |
| 542 | * @param string $tooltip_title, $tooltip_desc, $tooltip_code |
| 543 | * @param mixed $tooltip_anchor |
| 544 | * @param mixed $tooltip_desc |
| 545 | * |
| 546 | * @return string tooltip title, tooltip description, tooltip url |
| 547 | */ |
| 548 | function seopress_tooltip_link( $tooltip_anchor, $tooltip_desc ) { |
| 549 | $html = '<a href="' . $tooltip_anchor . '" |
| 550 | target="_blank" class="seopress-doc"> |
| 551 | <span class="dashicons dashicons-editor-help"></span> |
| 552 | <span class="screen-reader-text"> |
| 553 | ' . $tooltip_desc . ' |
| 554 | </span> |
| 555 | </a>'; |
| 556 | |
| 557 | return $html; |
| 558 | } |
| 559 | |
| 560 | /** |
| 561 | * Remove BOM. |
| 562 | * |
| 563 | * @param mixed $text |
| 564 | * |
| 565 | * @return mixed $text |
| 566 | */ |
| 567 | function seopress_remove_utf8_bom( $text ) { |
| 568 | $bom = pack( 'H*', 'EFBBBF' ); |
| 569 | $text = preg_replace( "/^$bom/", '', $text ); |
| 570 | |
| 571 | return $text; |
| 572 | } |
| 573 | |
| 574 | /** |
| 575 | * Filter the capability to allow other roles to use the plugin. |
| 576 | * |
| 577 | * @return string |
| 578 | * |
| 579 | * @param mixed $cap |
| 580 | * @param mixed $context |
| 581 | */ |
| 582 | function seopress_capability( $cap, $context = '' ) { |
| 583 | $newcap = apply_filters( 'seopress_capability', $cap, $context ); |
| 584 | |
| 585 | if ( ! current_user_can( $newcap ) ) { |
| 586 | return $cap; |
| 587 | } |
| 588 | |
| 589 | return $newcap; |
| 590 | } |
| 591 | |
| 592 | /** |
| 593 | * Check if the page is one of ours. |
| 594 | * |
| 595 | * @return bool |
| 596 | */ |
| 597 | function is_seopress_page() { |
| 598 | if ( ! is_admin() ) { |
| 599 | return false; |
| 600 | } |
| 601 | |
| 602 | $page = isset( $_REQUEST['page'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['page'] ) ) : null; |
| 603 | $post_type = isset( $_REQUEST['post_type'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ) ) : null; |
| 604 | |
| 605 | if ( $page ) { |
| 606 | return strpos( $page, 'seopress' ) === 0; |
| 607 | } |
| 608 | |
| 609 | if ( $post_type ) { |
| 610 | if ( is_array( $post_type ) && ! empty( $post_type ) ) { |
| 611 | return strpos( $post_type[0], 'seopress' ) === 0; |
| 612 | } |
| 613 | return strpos( $post_type, 'seopress' ) === 0; |
| 614 | } |
| 615 | |
| 616 | return false; |
| 617 | } |
| 618 | |
| 619 | /** |
| 620 | * Only add our notices on our pages. |
| 621 | * |
| 622 | * @since 3.8.2 |
| 623 | * |
| 624 | * @return bool |
| 625 | */ |
| 626 | function seopress_remove_other_notices() { |
| 627 | if ( is_seopress_page() ) { |
| 628 | remove_all_actions( 'network_admin_notices' ); |
| 629 | remove_all_actions( 'admin_notices' ); |
| 630 | remove_all_actions( 'user_admin_notices' ); |
| 631 | remove_all_actions( 'all_admin_notices' ); |
| 632 | add_action( 'admin_notices', 'seopress_admin_notices' ); |
| 633 | if ( is_plugin_active( 'wp-seopress-pro/seopress-pro.php' ) ) { |
| 634 | if ( version_compare( SEOPRESS_PRO_VERSION, '6.4', '>=' ) ) { |
| 635 | add_action( 'admin_notices', 'seopress_pro_admin_notices' ); |
| 636 | } |
| 637 | } |
| 638 | if ( is_plugin_active( 'wp-seopress-insights/seopress-insights.php' ) ) { |
| 639 | if ( version_compare( SEOPRESS_INSIGHTS_VERSION, '1.8.1', '>=' ) ) { |
| 640 | add_action( 'admin_notices', 'seopress_insights_notices' ); |
| 641 | } |
| 642 | } |
| 643 | } |
| 644 | } |
| 645 | add_action( 'in_admin_header', 'seopress_remove_other_notices', 1000 ); // Keep this value high to remove other notices. |
| 646 | |
| 647 | /** |
| 648 | * Only add our notices on our pages. |
| 649 | * |
| 650 | * @since 8.2.0 |
| 651 | * |
| 652 | * @return bool |
| 653 | */ |
| 654 | function seopress_remove_other_plugin_notices() { |
| 655 | if ( is_seopress_page() ) { |
| 656 | // SEOKEY plugin doesn't hook properly, we have to make a specific case. |
| 657 | remove_all_filters( 'seokey_filter_admin_notices_launch', 10 ); |
| 658 | } |
| 659 | } |
| 660 | add_action( 'admin_init', 'seopress_remove_other_plugin_notices' ); |
| 661 | |
| 662 | /** |
| 663 | * We replace the WP action by ours. |
| 664 | * |
| 665 | * @since 3.8.2 |
| 666 | * |
| 667 | * @return bool |
| 668 | */ |
| 669 | function seopress_admin_notices() { |
| 670 | do_action( 'seopress_admin_notices' ); |
| 671 | } |
| 672 | |
| 673 | /** |
| 674 | * Check if a key exists in a multidimensional array. |
| 675 | * |
| 676 | * @since 3.8.2 |
| 677 | * |
| 678 | * @return bool |
| 679 | * |
| 680 | * @param mixed $key |
| 681 | */ |
| 682 | function seopress_if_key_exists( array $arr, $key ) { |
| 683 | // is in base array? |
| 684 | if ( array_key_exists( $key, $arr ) ) { |
| 685 | return true; |
| 686 | } |
| 687 | |
| 688 | // Check arrays contained in this array. |
| 689 | foreach ( $arr as $element ) { |
| 690 | if ( is_array( $element ) ) { |
| 691 | if ( seopress_if_key_exists( $element, $key ) ) { |
| 692 | return true; |
| 693 | } |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | return false; |
| 698 | } |
| 699 | |
| 700 | /** |
| 701 | * Output submit button. |
| 702 | * |
| 703 | * @since 5.0 |
| 704 | * |
| 705 | * @param mixed $value |
| 706 | * @param mixed $classes |
| 707 | * @param mixed $type |
| 708 | */ |
| 709 | function sp_submit_button( $value = '', $classes = 'btn btnPrimary', $type = 'submit' ) { |
| 710 | if ( '' === $value ) { |
| 711 | $value = __( 'Save changes', 'wp-seopress' ); |
| 712 | } |
| 713 | |
| 714 | // Use esc_attr_e to escape attributes in the output. |
| 715 | $html = '<p class="submit"><input id="submit" name="submit" type="' . esc_attr( $type ) . '" class="' . esc_attr( $classes ) . '" value="' . esc_attr( $value ) . '"/></p>'; |
| 716 | |
| 717 | echo $html; |
| 718 | } |
| 719 | |
| 720 | |
| 721 | /** |
| 722 | * Generate HTML buttons classes |
| 723 | * |
| 724 | * @since 5.0 |
| 725 | * |
| 726 | * @return |
| 727 | */ |
| 728 | function seopress_btn_secondary_classes() { |
| 729 | // Classic Editor compatibility. |
| 730 | global $pagenow; |
| 731 | if ( function_exists( 'get_current_screen' ) && method_exists( get_current_screen(), 'is_block_editor' ) && true === get_current_screen()->is_block_editor() ) { |
| 732 | $btn_classes_secondary = 'components-button is-secondary'; |
| 733 | } elseif ( isset( $pagenow ) && ( $pagenow === 'term.php' || $pagenow === 'post.php' || $pagenow === 'post-new.php' ) ) { |
| 734 | $btn_classes_secondary = 'button button-secondary'; |
| 735 | } else { |
| 736 | $btn_classes_secondary = 'btn btnSecondary'; |
| 737 | } |
| 738 | |
| 739 | return $btn_classes_secondary; |
| 740 | } |
| 741 | |
| 742 | /** |
| 743 | * Global check. |
| 744 | * |
| 745 | * @since 3.8 |
| 746 | * |
| 747 | * @param string $feature Feature name to check. |
| 748 | * |
| 749 | * @return string|null Toggle option value if exists, null otherwise. |
| 750 | */ |
| 751 | function seopress_get_toggle_option( $feature ) { |
| 752 | $seopress_get_toggle_option = get_option( 'seopress_toggle' ); |
| 753 | if ( ! empty( $seopress_get_toggle_option ) ) { |
| 754 | foreach ( $seopress_get_toggle_option as $key => $seopress_get_toggle_value ) { |
| 755 | $options[ $key ] = $seopress_get_toggle_value; |
| 756 | if ( isset( $seopress_get_toggle_option[ 'toggle-' . $feature ] ) ) { |
| 757 | return $seopress_get_toggle_option[ 'toggle-' . $feature ]; |
| 758 | } |
| 759 | } |
| 760 | } |
| 761 | } |
| 762 | |
| 763 | /** |
| 764 | * Disable Add to cart GA tracking code on archive page / related products for Elementor PRO to avoid a JS conflict. |
| 765 | * |
| 766 | * @since 5.3 |
| 767 | * @return empty string |
| 768 | */ |
| 769 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; |
| 770 | if ( is_plugin_active( 'elementor-pro/elementor-pro.php' ) ) { |
| 771 | add_filter( 'seopress_gtag_ec_add_to_cart_archive_ev', 'sp_elementor_gtag_ec_add_to_cart_archive_ev' ); |
| 772 | function sp_elementor_gtag_ec_add_to_cart_archive_ev( $js ) { |
| 773 | return ''; |
| 774 | } |
| 775 | } |
| 776 | |
| 777 | /** |
| 778 | * Helper function needed for PHP 8.1 compatibility with "current" function |
| 779 | * Get mangled object vars. |
| 780 | * |
| 781 | * @since 6.2.0 |
| 782 | */ |
| 783 | function seopress_maybe_mangled_object_vars( $data ) { |
| 784 | if ( ! function_exists( 'get_mangled_object_vars' ) ) { |
| 785 | return $data; |
| 786 | } |
| 787 | |
| 788 | if ( ! is_object( $data ) ) { |
| 789 | return $data; |
| 790 | } |
| 791 | |
| 792 | return get_mangled_object_vars( $data ); |
| 793 | } |
| 794 | |
| 795 | /** |
| 796 | * Generate dynamically the Instant Indexing API key |
| 797 | * |
| 798 | * @since 8.6.0 |
| 799 | * |
| 800 | * @param bool $init |
| 801 | * |
| 802 | * @return void |
| 803 | */ |
| 804 | function seopress_instant_indexing_generate_api_key_fn( $init = false ) { |
| 805 | $options = get_option( 'seopress_instant_indexing_option_name' ) ? get_option( 'seopress_instant_indexing_option_name' ) : array(); |
| 806 | |
| 807 | $api_key = wp_generate_uuid4(); |
| 808 | $api_key = preg_replace( '[-]', '', $api_key ); |
| 809 | $options['seopress_instant_indexing_bing_api_key'] = base64_encode( $api_key ); |
| 810 | |
| 811 | if ( true === $init ) { |
| 812 | $options['seopress_instant_indexing_automate_submission'] = '1'; |
| 813 | } |
| 814 | |
| 815 | update_option( 'seopress_instant_indexing_option_name', $options ); |
| 816 | |
| 817 | if ( false === $init ) { |
| 818 | wp_send_json_success(); |
| 819 | } |
| 820 | } |
| 821 |