/wp-content/plugins/wordpress-seo/inc/class-wpseo-utils.php

https://bitbucket.org/carloskikea/helpet · PHP · 1091 lines · 502 code · 121 blank · 468 comment · 88 complexity · f493cb59425354943bc3755c04b59a90 MD5 · raw file

  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Internals
  6. * @since 1.8.0
  7. */
  8. /**
  9. * Group of utility methods for use by WPSEO.
  10. * All methods are static, this is just a sort of namespacing class wrapper.
  11. */
  12. class WPSEO_Utils {
  13. /**
  14. * @var bool $has_filters Whether the PHP filter extension is enabled.
  15. * @static
  16. * @since 1.8.0
  17. */
  18. public static $has_filters;
  19. /**
  20. * @var array Notifications to be shown in the JavaScript console.
  21. * @static
  22. * @since 3.3.2
  23. */
  24. protected static $console_notifications = array();
  25. /**
  26. * Check whether the current user is allowed to access the configuration.
  27. *
  28. * @static
  29. *
  30. * @since 1.8.0
  31. *
  32. * @return boolean
  33. */
  34. public static function grant_access() {
  35. // @todo Add deprecation warning.
  36. if ( ! is_multisite() ) {
  37. return true;
  38. }
  39. $options = get_site_option( 'wpseo_ms' );
  40. if ( empty( $options['access'] ) || $options['access'] === 'admin' ) {
  41. return current_user_can( 'wpseo_manage_options' );
  42. }
  43. return is_super_admin();
  44. }
  45. /**
  46. * Check whether file editing is allowed for the .htaccess and robots.txt files.
  47. *
  48. * {@internal current_user_can() checks internally whether a user is on wp-ms and adjusts accordingly.}}
  49. *
  50. * @static
  51. *
  52. * @since 1.8.0
  53. *
  54. * @return bool
  55. */
  56. public static function allow_system_file_edit() {
  57. $allowed = true;
  58. if ( current_user_can( 'edit_files' ) === false ) {
  59. $allowed = false;
  60. }
  61. /**
  62. * Filter: 'wpseo_allow_system_file_edit' - Allow developers to change whether the editing of
  63. * .htaccess and robots.txt is allowed.
  64. *
  65. * @api bool $allowed Whether file editing is allowed.
  66. */
  67. return apply_filters( 'wpseo_allow_system_file_edit', $allowed );
  68. }
  69. /**
  70. * Check if the web server is running on Apache.
  71. *
  72. * @static
  73. *
  74. * @since 1.8.0
  75. *
  76. * @return bool
  77. */
  78. public static function is_apache() {
  79. if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'apache' ) !== false ) {
  80. return true;
  81. }
  82. return false;
  83. }
  84. /**
  85. * Check if the web server is running on Nginx.
  86. *
  87. * @static
  88. *
  89. * @since 1.8.0
  90. *
  91. * @return bool
  92. */
  93. public static function is_nginx() {
  94. if ( isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( $_SERVER['SERVER_SOFTWARE'], 'nginx' ) !== false ) {
  95. return true;
  96. }
  97. return false;
  98. }
  99. /**
  100. * Register a notification to be shown in the JavaScript console.
  101. *
  102. * @since 3.3.2
  103. *
  104. * @param string $identifier Notification identifier.
  105. * @param string $message Message to be shown.
  106. * @param bool $one_time_only Only show once (if added multiple times).
  107. */
  108. public static function javascript_console_notification( $identifier, $message, $one_time_only = false ) {
  109. static $registered_hook;
  110. if ( is_null( $registered_hook ) ) {
  111. add_action( 'admin_footer', array( __CLASS__, 'localize_console_notices' ), 999 );
  112. $registered_hook = true;
  113. }
  114. $prefix = 'Yoast SEO: ';
  115. if ( substr( $message, 0, strlen( $prefix ) ) !== $prefix ) {
  116. $message = $prefix . $message;
  117. }
  118. if ( $one_time_only ) {
  119. self::$console_notifications[ $identifier ] = $message;
  120. }
  121. else {
  122. self::$console_notifications[] = $message;
  123. }
  124. }
  125. /**
  126. * Localize the console notifications to JavaScript.
  127. *
  128. * @since 3.3.2
  129. */
  130. public static function localize_console_notices() {
  131. if ( empty( self::$console_notifications ) ) {
  132. return;
  133. }
  134. wp_localize_script( WPSEO_Admin_Asset_Manager::PREFIX . 'admin-global-script', 'wpseoConsoleNotifications', array_values( self::$console_notifications ) );
  135. }
  136. /**
  137. * Check whether a url is relative.
  138. *
  139. * @since 1.8.0
  140. *
  141. * @param string $url URL string to check.
  142. *
  143. * @return bool
  144. */
  145. public static function is_url_relative( $url ) {
  146. return ( strpos( $url, 'http' ) !== 0 && strpos( $url, '//' ) !== 0 );
  147. }
  148. /**
  149. * List all the available user roles.
  150. *
  151. * @since 1.8.0
  152. *
  153. * @static
  154. *
  155. * @return array $roles
  156. */
  157. public static function get_roles() {
  158. global $wp_roles;
  159. if ( ! isset( $wp_roles ) ) {
  160. $wp_roles = new WP_Roles();
  161. }
  162. $roles = $wp_roles->get_names();
  163. return $roles;
  164. }
  165. /**
  166. * Standardize whitespace in a string.
  167. *
  168. * Replace line breaks, carriage returns, tabs with a space, then remove double spaces.
  169. *
  170. * @since 1.8.0
  171. *
  172. * @param string $string String input to standardize.
  173. *
  174. * @return string
  175. */
  176. public static function standardize_whitespace( $string ) {
  177. return trim( str_replace( ' ', ' ', str_replace( array( "\t", "\n", "\r", "\f" ), ' ', $string ) ) );
  178. }
  179. /**
  180. * First strip out registered and enclosing shortcodes using native WordPress strip_shortcodes function.
  181. * Then strip out the shortcodes with a filthy regex, because people don't properly register their shortcodes.
  182. *
  183. * @static
  184. *
  185. * @since 1.8.0
  186. *
  187. * @param string $text Input string that might contain shortcodes.
  188. *
  189. * @return string $text String without shortcodes.
  190. */
  191. public static function strip_shortcode( $text ) {
  192. return preg_replace( '`\[[^\]]+\]`s', '', strip_shortcodes( $text ) );
  193. }
  194. /**
  195. * Recursively trim whitespace round a string value or of string values within an array.
  196. * Only trims strings to avoid typecasting a variable (to string).
  197. *
  198. * @static
  199. *
  200. * @since 1.8.0
  201. *
  202. * @param mixed $value Value to trim or array of values to trim.
  203. *
  204. * @return mixed Trimmed value or array of trimmed values.
  205. */
  206. public static function trim_recursive( $value ) {
  207. if ( is_string( $value ) ) {
  208. $value = trim( $value );
  209. }
  210. elseif ( is_array( $value ) ) {
  211. $value = array_map( array( __CLASS__, 'trim_recursive' ), $value );
  212. }
  213. return $value;
  214. }
  215. /**
  216. * Translates a decimal analysis score into a textual one.
  217. *
  218. * @static
  219. *
  220. * @since 1.8.0
  221. *
  222. * @param int $val The decimal score to translate.
  223. * @param bool $css_value Whether to return the i18n translated score or the CSS class value.
  224. *
  225. * @return string
  226. */
  227. public static function translate_score( $val, $css_value = true ) {
  228. $seo_rank = WPSEO_Rank::from_numeric_score( $val );
  229. if ( $css_value ) {
  230. return $seo_rank->get_css_class();
  231. }
  232. return $seo_rank->get_label();
  233. }
  234. /**
  235. * Emulate the WP native sanitize_text_field function in a %%variable%% safe way.
  236. *
  237. * @see https://core.trac.wordpress.org/browser/trunk/src/wp-includes/formatting.php for the original
  238. *
  239. * Sanitize a string from user input or from the db.
  240. *
  241. * - Check for invalid UTF-8,
  242. * - Convert single < characters to entity,
  243. * - Strip all tags,
  244. * - Remove line breaks, tabs and extra white space,
  245. * - Strip octets - BUT DO NOT REMOVE (part of) VARIABLES WHICH WILL BE REPLACED.
  246. *
  247. * @static
  248. *
  249. * @since 1.8.0
  250. *
  251. * @param string $value String value to sanitize.
  252. *
  253. * @return string
  254. */
  255. public static function sanitize_text_field( $value ) {
  256. $filtered = wp_check_invalid_utf8( $value );
  257. if ( strpos( $filtered, '<' ) !== false ) {
  258. $filtered = wp_pre_kses_less_than( $filtered );
  259. // This will strip extra whitespace for us.
  260. $filtered = wp_strip_all_tags( $filtered, true );
  261. }
  262. else {
  263. $filtered = trim( preg_replace( '`[\r\n\t ]+`', ' ', $filtered ) );
  264. }
  265. $found = false;
  266. while ( preg_match( '`[^%](%[a-f0-9]{2})`i', $filtered, $match ) ) {
  267. $filtered = str_replace( $match[1], '', $filtered );
  268. $found = true;
  269. }
  270. unset( $match );
  271. if ( $found ) {
  272. // Strip out the whitespace that may now exist after removing the octets.
  273. $filtered = trim( preg_replace( '` +`', ' ', $filtered ) );
  274. }
  275. /**
  276. * Filter a sanitized text field string.
  277. *
  278. * @since WP 2.9.0
  279. *
  280. * @param string $filtered The sanitized string.
  281. * @param string $str The string prior to being sanitized.
  282. */
  283. return apply_filters( 'sanitize_text_field', $filtered, $value );
  284. }
  285. /**
  286. * Sanitize a url for saving to the database.
  287. * Not to be confused with the old native WP function.
  288. *
  289. * @todo [JRF => whomever] Check/improve url verification.
  290. *
  291. * @since 1.8.0
  292. *
  293. * @param string $value String URL value to sanitize.
  294. * @param array $allowed_protocols Optional set of allowed protocols.
  295. *
  296. * @return string
  297. */
  298. public static function sanitize_url( $value, $allowed_protocols = array( 'http', 'https' ) ) {
  299. return esc_url_raw( sanitize_text_field( rawurldecode( $value ) ), $allowed_protocols );
  300. }
  301. /**
  302. * Validate a value as boolean.
  303. *
  304. * @static
  305. *
  306. * @since 1.8.0
  307. *
  308. * @param mixed $value Value to validate.
  309. *
  310. * @return bool
  311. */
  312. public static function validate_bool( $value ) {
  313. if ( ! isset( self::$has_filters ) ) {
  314. self::$has_filters = extension_loaded( 'filter' );
  315. }
  316. if ( self::$has_filters ) {
  317. return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
  318. }
  319. else {
  320. return self::emulate_filter_bool( $value );
  321. }
  322. }
  323. /**
  324. * Cast a value to bool.
  325. *
  326. * @static
  327. *
  328. * @since 1.8.0
  329. *
  330. * @param mixed $value Value to cast.
  331. *
  332. * @return bool
  333. */
  334. public static function emulate_filter_bool( $value ) {
  335. $true = array(
  336. '1',
  337. 'true',
  338. 'True',
  339. 'TRUE',
  340. 'y',
  341. 'Y',
  342. 'yes',
  343. 'Yes',
  344. 'YES',
  345. 'on',
  346. 'On',
  347. 'ON',
  348. );
  349. $false = array(
  350. '0',
  351. 'false',
  352. 'False',
  353. 'FALSE',
  354. 'n',
  355. 'N',
  356. 'no',
  357. 'No',
  358. 'NO',
  359. 'off',
  360. 'Off',
  361. 'OFF',
  362. );
  363. if ( is_bool( $value ) ) {
  364. return $value;
  365. }
  366. elseif ( is_int( $value ) && ( $value === 0 || $value === 1 ) ) {
  367. return (bool) $value;
  368. }
  369. elseif ( ( is_float( $value ) && ! is_nan( $value ) ) && ( $value === (float) 0 || $value === (float) 1 ) ) {
  370. return (bool) $value;
  371. }
  372. elseif ( is_string( $value ) ) {
  373. $value = trim( $value );
  374. if ( in_array( $value, $true, true ) ) {
  375. return true;
  376. }
  377. elseif ( in_array( $value, $false, true ) ) {
  378. return false;
  379. }
  380. else {
  381. return false;
  382. }
  383. }
  384. return false;
  385. }
  386. /**
  387. * Validate a value as integer.
  388. *
  389. * @static
  390. *
  391. * @since 1.8.0
  392. *
  393. * @param mixed $value Value to validate.
  394. *
  395. * @return int|bool Int or false in case of failure to convert to int.
  396. */
  397. public static function validate_int( $value ) {
  398. if ( ! isset( self::$has_filters ) ) {
  399. self::$has_filters = extension_loaded( 'filter' );
  400. }
  401. if ( self::$has_filters ) {
  402. return filter_var( $value, FILTER_VALIDATE_INT );
  403. }
  404. else {
  405. return self::emulate_filter_int( $value );
  406. }
  407. }
  408. /**
  409. * Cast a value to integer.
  410. *
  411. * @static
  412. *
  413. * @since 1.8.0
  414. *
  415. * @param mixed $value Value to cast.
  416. *
  417. * @return int|bool
  418. */
  419. public static function emulate_filter_int( $value ) {
  420. if ( is_int( $value ) ) {
  421. return $value;
  422. }
  423. elseif ( is_float( $value ) ) {
  424. if ( (int) $value == $value && ! is_nan( $value ) ) {
  425. return (int) $value;
  426. }
  427. else {
  428. return false;
  429. }
  430. }
  431. elseif ( is_string( $value ) ) {
  432. $value = trim( $value );
  433. if ( $value === '' ) {
  434. return false;
  435. }
  436. elseif ( ctype_digit( $value ) ) {
  437. return (int) $value;
  438. }
  439. elseif ( strpos( $value, '-' ) === 0 && ctype_digit( substr( $value, 1 ) ) ) {
  440. return (int) $value;
  441. }
  442. else {
  443. return false;
  444. }
  445. }
  446. return false;
  447. }
  448. /**
  449. * Clears the WP or W3TC cache depending on which is used.
  450. *
  451. * @static
  452. *
  453. * @since 1.8.0
  454. */
  455. public static function clear_cache() {
  456. if ( function_exists( 'w3tc_pgcache_flush' ) ) {
  457. w3tc_pgcache_flush();
  458. }
  459. elseif ( function_exists( 'wp_cache_clear_cache' ) ) {
  460. wp_cache_clear_cache();
  461. }
  462. }
  463. /**
  464. * Flush W3TC cache after succesfull update/add of taxonomy meta option.
  465. *
  466. * @static
  467. *
  468. * @since 1.8.0
  469. */
  470. public static function flush_w3tc_cache() {
  471. if ( defined( 'W3TC_DIR' ) && function_exists( 'w3tc_objectcache_flush' ) ) {
  472. w3tc_objectcache_flush();
  473. }
  474. }
  475. /**
  476. * Clear rewrite rules.
  477. *
  478. * @static
  479. *
  480. * @since 1.8.0
  481. */
  482. public static function clear_rewrites() {
  483. delete_option( 'rewrite_rules' );
  484. }
  485. /**
  486. * Do simple reliable math calculations without the risk of wrong results.
  487. *
  488. * @see http://floating-point-gui.de/
  489. * @see the big red warning on http://php.net/language.types.float.php
  490. *
  491. * In the rare case that the bcmath extension would not be loaded, it will return the normal calculation results.
  492. *
  493. * @static
  494. *
  495. * @since 1.5.0
  496. * @since 1.8.0 Moved from stand-alone function to this class.
  497. *
  498. * @param mixed $number1 Scalar (string/int/float/bool).
  499. * @param string $action Calculation action to execute. Valid input:
  500. * '+' or 'add' or 'addition',
  501. * '-' or 'sub' or 'subtract',
  502. * '*' or 'mul' or 'multiply',
  503. * '/' or 'div' or 'divide',
  504. * '%' or 'mod' or 'modulus'
  505. * '=' or 'comp' or 'compare'.
  506. * @param mixed $number2 Scalar (string/int/float/bool).
  507. * @param bool $round Whether or not to round the result. Defaults to false.
  508. * Will be disregarded for a compare operation.
  509. * @param int $decimals Decimals for rounding operation. Defaults to 0.
  510. * @param int $precision Calculation precision. Defaults to 10.
  511. *
  512. * @return mixed Calculation Result or false if either or the numbers isn't scalar or
  513. * an invalid operation was passed.
  514. * - for compare the result will always be an integer.
  515. * - for all other operations, the result will either be an integer (preferred)
  516. * or a float.
  517. */
  518. public static function calc( $number1, $action, $number2, $round = false, $decimals = 0, $precision = 10 ) {
  519. static $bc;
  520. if ( ! is_scalar( $number1 ) || ! is_scalar( $number2 ) ) {
  521. return false;
  522. }
  523. if ( ! isset( $bc ) ) {
  524. $bc = extension_loaded( 'bcmath' );
  525. }
  526. if ( $bc ) {
  527. $number1 = number_format( $number1, 10, '.', '' );
  528. $number2 = number_format( $number2, 10, '.', '' );
  529. }
  530. $result = null;
  531. $compare = false;
  532. switch ( $action ) {
  533. case '+':
  534. case 'add':
  535. case 'addition':
  536. $result = ( $bc ) ? bcadd( $number1, $number2, $precision ) /* string */ : ( $number1 + $number2 );
  537. break;
  538. case '-':
  539. case 'sub':
  540. case 'subtract':
  541. $result = ( $bc ) ? bcsub( $number1, $number2, $precision ) /* string */ : ( $number1 - $number2 );
  542. break;
  543. case '*':
  544. case 'mul':
  545. case 'multiply':
  546. $result = ( $bc ) ? bcmul( $number1, $number2, $precision ) /* string */ : ( $number1 * $number2 );
  547. break;
  548. case '/':
  549. case 'div':
  550. case 'divide':
  551. if ( $bc ) {
  552. $result = bcdiv( $number1, $number2, $precision ); // String, or NULL if right_operand is 0.
  553. }
  554. elseif ( $number2 != 0 ) {
  555. $result = ( $number1 / $number2 );
  556. }
  557. if ( ! isset( $result ) ) {
  558. $result = 0;
  559. }
  560. break;
  561. case '%':
  562. case 'mod':
  563. case 'modulus':
  564. if ( $bc ) {
  565. $result = bcmod( $number1, $number2 ); // String, or NULL if modulus is 0.
  566. }
  567. elseif ( $number2 != 0 ) {
  568. $result = ( $number1 % $number2 );
  569. }
  570. if ( ! isset( $result ) ) {
  571. $result = 0;
  572. }
  573. break;
  574. case '=':
  575. case 'comp':
  576. case 'compare':
  577. $compare = true;
  578. if ( $bc ) {
  579. $result = bccomp( $number1, $number2, $precision ); // Returns int 0, 1 or -1.
  580. }
  581. else {
  582. $result = ( $number1 == $number2 ) ? 0 : ( ( $number1 > $number2 ) ? 1 : - 1 );
  583. }
  584. break;
  585. }
  586. if ( isset( $result ) ) {
  587. if ( $compare === false ) {
  588. if ( $round === true ) {
  589. $result = round( floatval( $result ), $decimals );
  590. if ( $decimals === 0 ) {
  591. $result = (int) $result;
  592. }
  593. }
  594. else {
  595. $result = ( intval( $result ) == $result ) ? intval( $result ) : floatval( $result );
  596. }
  597. }
  598. return $result;
  599. }
  600. return false;
  601. }
  602. /**
  603. * Trim whitespace and NBSP (Non-breaking space) from string.
  604. *
  605. * @since 2.0.0
  606. *
  607. * @param string $string String input to trim.
  608. *
  609. * @return string
  610. */
  611. public static function trim_nbsp_from_string( $string ) {
  612. $find = array( '&nbsp;', chr( 0xC2 ) . chr( 0xA0 ) );
  613. $string = str_replace( $find, ' ', $string );
  614. $string = trim( $string );
  615. return $string;
  616. }
  617. /**
  618. * Check if a string is a valid datetime.
  619. *
  620. * @since 2.0.0
  621. *
  622. * @param string $datetime String input to check as valid input for DateTime class.
  623. *
  624. * @return bool
  625. */
  626. public static function is_valid_datetime( $datetime ) {
  627. if ( substr( $datetime, 0, 1 ) === '-' ) {
  628. return false;
  629. }
  630. try {
  631. return new DateTime( $datetime ) !== false;
  632. } catch ( Exception $exc ) {
  633. return false;
  634. }
  635. }
  636. /**
  637. * Format the URL to be sure it is okay for using as a redirect url.
  638. *
  639. * This method will parse the URL and combine them in one string.
  640. *
  641. * @since 2.3.0
  642. *
  643. * @param string $url URL string.
  644. *
  645. * @return mixed
  646. */
  647. public static function format_url( $url ) {
  648. $parsed_url = wp_parse_url( $url );
  649. $formatted_url = '';
  650. if ( ! empty( $parsed_url['path'] ) ) {
  651. $formatted_url = $parsed_url['path'];
  652. }
  653. // Prepend a slash if first char != slash.
  654. if ( stripos( $formatted_url, '/' ) !== 0 ) {
  655. $formatted_url = '/' . $formatted_url;
  656. }
  657. // Append 'query' string if it exists.
  658. if ( ! empty( $parsed_url['query'] ) ) {
  659. $formatted_url .= '?' . $parsed_url['query'];
  660. }
  661. return apply_filters( 'wpseo_format_admin_url', $formatted_url );
  662. }
  663. /**
  664. * Get plugin name from file.
  665. *
  666. * @since 2.3.3
  667. *
  668. * @param string $plugin Plugin path relative to plugins directory.
  669. *
  670. * @return string|bool
  671. */
  672. public static function get_plugin_name( $plugin ) {
  673. $plugin_details = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
  674. if ( $plugin_details['Name'] !== '' ) {
  675. return $plugin_details['Name'];
  676. }
  677. return false;
  678. }
  679. /**
  680. * Retrieves the sitename.
  681. *
  682. * @since 3.0.0
  683. *
  684. * @return string
  685. */
  686. public static function get_site_name() {
  687. return wp_strip_all_tags( get_bloginfo( 'name' ), true );
  688. }
  689. /**
  690. * Retrieves the title separator.
  691. *
  692. * @since 3.0.0
  693. *
  694. * @return string
  695. */
  696. public static function get_title_separator() {
  697. $replacement = WPSEO_Options::get_default( 'wpseo_titles', 'separator' );
  698. // Get the titles option and the separator options.
  699. $separator = WPSEO_Options::get( 'separator' );
  700. $seperator_options = WPSEO_Option_Titles::get_instance()->get_separator_options();
  701. // This should always be set, but just to be sure.
  702. if ( isset( $seperator_options[ $separator ] ) ) {
  703. // Set the new replacement.
  704. $replacement = $seperator_options[ $separator ];
  705. }
  706. /**
  707. * Filter: 'wpseo_replacements_filter_sep' - Allow customization of the separator character(s).
  708. *
  709. * @api string $replacement The current separator.
  710. */
  711. return apply_filters( 'wpseo_replacements_filter_sep', $replacement );
  712. }
  713. /**
  714. * Check if the current opened page is a Yoast SEO page.
  715. *
  716. * @since 3.0.0
  717. *
  718. * @return bool
  719. */
  720. public static function is_yoast_seo_page() {
  721. static $is_yoast_seo;
  722. if ( $is_yoast_seo === null ) {
  723. $current_page = filter_input( INPUT_GET, 'page' );
  724. $is_yoast_seo = ( substr( $current_page, 0, 6 ) === 'wpseo_' );
  725. }
  726. return $is_yoast_seo;
  727. }
  728. /**
  729. * Check if the current opened page belongs to Yoast SEO Free.
  730. *
  731. * @since 3.3.0
  732. *
  733. * @param string $current_page The current page the user is on.
  734. *
  735. * @return bool
  736. */
  737. public static function is_yoast_seo_free_page( $current_page ) {
  738. $yoast_seo_free_pages = array(
  739. 'wpseo_dashboard',
  740. 'wpseo_titles',
  741. 'wpseo_social',
  742. 'wpseo_advanced',
  743. 'wpseo_tools',
  744. 'wpseo_search_console',
  745. 'wpseo_licenses',
  746. );
  747. return in_array( $current_page, $yoast_seo_free_pages, true );
  748. }
  749. /**
  750. * Checks if we are in the premium or free plugin.
  751. *
  752. * @return bool True when we are in the premium plugin.
  753. */
  754. public static function is_yoast_seo_premium() {
  755. return defined( 'WPSEO_PREMIUM_PLUGIN_FILE' );
  756. }
  757. /**
  758. * Determine if Yoast SEO is in development mode?
  759. *
  760. * Inspired by JetPack (https://github.com/Automattic/jetpack/blob/master/class.jetpack.php#L1383-L1406).
  761. *
  762. * @since 3.0.0
  763. *
  764. * @return bool
  765. */
  766. public static function is_development_mode() {
  767. $development_mode = false;
  768. if ( defined( 'WPSEO_DEBUG' ) ) {
  769. $development_mode = WPSEO_DEBUG;
  770. }
  771. elseif ( site_url() && false === strpos( site_url(), '.' ) ) {
  772. $development_mode = true;
  773. }
  774. /**
  775. * Filter the Yoast SEO development mode.
  776. *
  777. * @since 3.0
  778. *
  779. * @param bool $development_mode Is Yoast SEOs development mode active.
  780. */
  781. return apply_filters( 'yoast_seo_development_mode', $development_mode );
  782. }
  783. /**
  784. * Retrieve home URL with proper trailing slash.
  785. *
  786. * @since 3.3.0
  787. *
  788. * @param string $path Path relative to home URL.
  789. * @param string|null $scheme Scheme to apply.
  790. *
  791. * @return string Home URL with optional path, appropriately slashed if not.
  792. */
  793. public static function home_url( $path = '', $scheme = null ) {
  794. $home_url = home_url( $path, $scheme );
  795. if ( ! empty( $path ) ) {
  796. return $home_url;
  797. }
  798. $home_path = wp_parse_url( $home_url, PHP_URL_PATH );
  799. if ( '/' === $home_path ) { // Home at site root, already slashed.
  800. return $home_url;
  801. }
  802. if ( is_null( $home_path ) ) { // Home at site root, always slash.
  803. return trailingslashit( $home_url );
  804. }
  805. if ( is_string( $home_path ) ) { // Home in subdirectory, slash if permalink structure has slash.
  806. return user_trailingslashit( $home_url );
  807. }
  808. return $home_url;
  809. }
  810. /**
  811. * Returns a base64 URL for the svg for use in the menu.
  812. *
  813. * @since 3.3.0
  814. *
  815. * @param bool $base64 Whether or not to return base64'd output.
  816. *
  817. * @return string
  818. */
  819. public static function get_icon_svg( $base64 = true ) {
  820. $svg = '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="100%" height="100%" style="fill:#82878c" viewBox="0 0 512 512" role="img" aria-hidden="true" focusable="false"><g><g><g><g><path d="M203.6,395c6.8-17.4,6.8-36.6,0-54l-79.4-204h70.9l47.7,149.4l74.8-207.6H116.4c-41.8,0-76,34.2-76,76V357c0,41.8,34.2,76,76,76H173C189,424.1,197.6,410.3,203.6,395z"/></g><g><path d="M471.6,154.8c0-41.8-34.2-76-76-76h-3L285.7,365c-9.6,26.7-19.4,49.3-30.3,68h216.2V154.8z"/></g></g><path stroke-width="2.974" stroke-miterlimit="10" d="M338,1.3l-93.3,259.1l-42.1-131.9h-89.1l83.8,215.2c6,15.5,6,32.5,0,48c-7.4,19-19,37.3-53,41.9l-7.2,1v76h8.3c81.7,0,118.9-57.2,149.6-142.9L431.6,1.3H338z M279.4,362c-32.9,92-67.6,128.7-125.7,131.8v-45c37.5-7.5,51.3-31,59.1-51.1c7.5-19.3,7.5-40.7,0-60l-75-192.7h52.8l53.3,166.8l105.9-294h58.1L279.4,362z"/></g></g></svg>';
  821. if ( $base64 ) {
  822. return 'data:image/svg+xml;base64,' . base64_encode( $svg );
  823. }
  824. return $svg;
  825. }
  826. /**
  827. * Returns the SVG for the traffic light in the metabox.
  828. *
  829. * @return string
  830. */
  831. public static function traffic_light_svg() {
  832. return <<<SVG
  833. <svg class="yst-traffic-light init" version="1.1" xmlns="http://www.w3.org/2000/svg"
  834. role="img" aria-hidden="true" focusable="false"
  835. x="0px" y="0px" viewBox="0 0 30 47" enable-background="new 0 0 30 47" xml:space="preserve">
  836. <g id="BG_1_">
  837. </g>
  838. <g id="traffic_light">
  839. <g>
  840. <g>
  841. <g>
  842. <path fill="#5B2942" d="M22,0H8C3.6,0,0,3.6,0,7.9v31.1C0,43.4,3.6,47,8,47h14c4.4,0,8-3.6,8-7.9V7.9C30,3.6,26.4,0,22,0z
  843. M27.5,38.8c0,3.1-2.6,5.7-5.8,5.7H8.3c-3.2,0-5.8-2.5-5.8-5.7V8.3c0-1.5,0.6-2.9,1.7-4c1.1-1,2.5-1.6,4.1-1.6h13.4
  844. c1.5,0,3,0.6,4.1,1.6c1.1,1.1,1.7,2.5,1.7,4V38.8z"/>
  845. </g>
  846. <g class="traffic-light-color traffic-light-red">
  847. <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
  848. <ellipse fill="#DC3232" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
  849. <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
  850. </g>
  851. <g class="traffic-light-color traffic-light-orange">
  852. <ellipse fill="#F49A00" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
  853. <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
  854. <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
  855. </g>
  856. <g class="traffic-light-color traffic-light-green">
  857. <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
  858. <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
  859. <ellipse fill="#63B22B" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
  860. </g>
  861. <g class="traffic-light-color traffic-light-empty">
  862. <ellipse fill="#C8C8C8" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
  863. <ellipse fill="#C8C8C8" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
  864. <ellipse fill="#C8C8C8" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
  865. </g>
  866. <g class="traffic-light-color traffic-light-init">
  867. <ellipse fill="#5B2942" cx="15" cy="23.5" rx="5.7" ry="5.6"/>
  868. <ellipse fill="#5B2942" cx="15" cy="10.9" rx="5.7" ry="5.6"/>
  869. <ellipse fill="#5B2942" cx="15" cy="36.1" rx="5.7" ry="5.6"/>
  870. </g>
  871. </g>
  872. </g>
  873. </g>
  874. </svg>
  875. SVG;
  876. }
  877. /**
  878. * Checks if the WP-REST-API is available.
  879. *
  880. * @since 3.6
  881. * @since 3.7 Introduced the $minimum_version parameter.
  882. *
  883. * @param string $minimum_version The minimum version the API should be.
  884. *
  885. * @return bool Returns true if the API is available.
  886. */
  887. public static function is_api_available( $minimum_version = '2.0' ) {
  888. return ( defined( 'REST_API_VERSION' )
  889. && version_compare( REST_API_VERSION, $minimum_version, '>=' ) );
  890. }
  891. /********************** DEPRECATED METHODS **********************/
  892. /**
  893. * Returns the language part of a given locale, defaults to english when the $locale is empty.
  894. *
  895. * @see WPSEO_Language_Utils::get_language()
  896. *
  897. * @since 3.4
  898. *
  899. * @param string $locale The locale to get the language of.
  900. *
  901. * @returns string The language part of the locale.
  902. */
  903. public static function get_language( $locale ) {
  904. return WPSEO_Language_Utils::get_language( $locale );
  905. }
  906. /**
  907. * Returns the user locale for the language to be used in the admin.
  908. *
  909. * WordPress 4.7 introduced the ability for users to specify an Admin language
  910. * different from the language used on the front end. This checks if the feature
  911. * is available and returns the user's language, with a fallback to the site's language.
  912. * Can be removed when support for WordPress 4.6 will be dropped, in favor
  913. * of WordPress get_user_locale() that already fallbacks to the site's locale.
  914. *
  915. * @see WPSEO_Language_Utils::get_user_locale()
  916. *
  917. * @since 4.1
  918. *
  919. * @returns string The locale.
  920. */
  921. public static function get_user_locale() {
  922. return WPSEO_Language_Utils::get_user_locale();
  923. }
  924. /**
  925. * Determine whether or not the metabox should be displayed for a post type.
  926. *
  927. * @param string|null $post_type Optional. The post type to check the visibility of the metabox for.
  928. *
  929. * @return bool Whether or not the metabox should be displayed.
  930. */
  931. protected static function display_post_type_metabox( $post_type = null ) {
  932. if ( ! isset( $post_type ) ) {
  933. $post_type = get_post_type();
  934. }
  935. if ( ! isset( $post_type ) || ! WPSEO_Post_Type::is_post_type_accessible( $post_type ) ) {
  936. return false;
  937. }
  938. return WPSEO_Options::get( 'display-metabox-pt-' . $post_type );
  939. }
  940. /**
  941. * Determine whether or not the metabox should be displayed for a taxonomy.
  942. *
  943. * @param string|null $taxonomy Optional. The post type to check the visibility of the metabox for.
  944. *
  945. * @return bool Whether or not the metabox should be displayed.
  946. */
  947. protected static function display_taxonomy_metabox( $taxonomy = null ) {
  948. if ( ! isset( $taxonomy ) || ! in_array( $taxonomy, get_taxonomies( array( 'public' => true ), 'names' ), true ) ) {
  949. return false;
  950. }
  951. return WPSEO_Options::get( 'display-metabox-tax-' . $taxonomy );
  952. }
  953. /**
  954. * Determines whether the metabox is active for the given identifier and type.
  955. *
  956. * @param string $identifier The identifier to check for.
  957. * @param string $type The type to check for.
  958. *
  959. * @return bool Whether or not the metabox is active.
  960. */
  961. public static function is_metabox_active( $identifier, $type ) {
  962. if ( $type === 'post_type' ) {
  963. return self::display_post_type_metabox( $identifier );
  964. }
  965. if ( $type === 'taxonomy' ) {
  966. return self::display_taxonomy_metabox( $identifier );
  967. }
  968. return false;
  969. }
  970. }