/inc/class-wpseo-replace-vars.php

  1. <?php 
  2. /** 
  3. * @package WPSEO\Internals 
  4. * @since 1.5.4 
  5. */ 
  6.  
  7. // Avoid direct calls to this file. 
  8. if ( ! defined( 'WPSEO_VERSION' ) ) { 
  9. header( 'Status: 403 Forbidden' ); 
  10. header( 'HTTP/1.1 403 Forbidden' ); 
  11. exit(); 
  12.  
  13. /** 
  14. * Class: WPSEO_Replace_Vars 
  15. * 
  16. * This class implements the replacing of `%%variable_placeholders%%` with their real value based on the current 
  17. * requested page/post/cpt/etc in text strings. 
  18. */ 
  19. class WPSEO_Replace_Vars { 
  20.  
  21. /** 
  22. * @var array Default post/page/cpt information 
  23. */ 
  24. protected $defaults = array( 
  25. 'ID' => '',  
  26. 'name' => '',  
  27. 'post_author' => '',  
  28. 'post_content' => '',  
  29. 'post_date' => '',  
  30. 'post_excerpt' => '',  
  31. 'post_modified' => '',  
  32. 'post_title' => '',  
  33. 'taxonomy' => '',  
  34. 'term_id' => '',  
  35. 'term404' => '',  
  36. ); 
  37.  
  38. /** 
  39. * @var object Current post/page/cpt information 
  40. */ 
  41. protected $args; 
  42.  
  43. /** 
  44. * @var array Help texts for use in WPSEO -> Titles and Meta's help tabs 
  45. */ 
  46. protected static $help_texts = array(); 
  47.  
  48. /** 
  49. * @var array Register of additional variable replacements registered by other plugins/themes 
  50. */ 
  51. protected static $external_replacements = array(); 
  52.  
  53.  
  54. /** 
  55. * Constructor 
  56. * 
  57. * @return \WPSEO_Replace_Vars 
  58. */ 
  59. public function __construct() { 
  60.  
  61.  
  62. /** 
  63. * Setup the help texts and external replacements as statics so they will be available to all instances 
  64. */ 
  65. public static function setup_statics_once() { 
  66. if ( self::$help_texts === array() ) { 
  67. self::set_basic_help_texts(); 
  68. self::set_advanced_help_texts(); 
  69.  
  70. if ( self::$external_replacements === array() ) { 
  71. /** 
  72. * Action: 'wpseo_register_extra_replacements' - Allows for registration of additional 
  73. * variables to replace 
  74. */ 
  75. do_action( 'wpseo_register_extra_replacements' ); 
  76.  
  77.  
  78. /** 
  79. * Register new replacement %%variables%% 
  80. * For use by other plugins/themes to register extra variables 
  81. * 
  82. * @see wpseo_register_var_replacement() for a usage example 
  83. * 
  84. * @param string $var The name of the variable to replace, i.e. '%%var%%' 
  85. * - the surrounding %% are optional. 
  86. * @param mixed $replace_function Function or method to call to retrieve the replacement value for the variable 
  87. * Uses the same format as add_filter/add_action function parameter and 
  88. * should *return* the replacement value. DON'T echo it. 
  89. * @param string $type Type of variable: 'basic' or 'advanced', defaults to 'advanced'. 
  90. * @param string $help_text Help text to be added to the help tab for this variable. 
  91. * 
  92. * @return bool Whether the replacement function was succesfully registered 
  93. */ 
  94. public static function register_replacement( $var, $replace_function, $type = 'advanced', $help_text = '' ) { 
  95. $success = false; 
  96.  
  97. if ( is_string( $var ) && $var !== '' ) { 
  98. $var = self::remove_var_delimiter( $var ); 
  99.  
  100. if ( preg_match( '`^[A-Z0-9_-]+$`i', $var ) === false ) { 
  101. trigger_error( __( 'A replacement variable can only contain alphanumeric characters, an underscore or a dash. Try renaming your variable.', 'wordpress-seo' ), E_USER_WARNING ); 
  102. elseif ( strpos( $var, 'cf_' ) === 0 || strpos( $var, 'ct_' ) === 0 ) { 
  103. trigger_error( __( 'A replacement variable can not start with "%%cf_" or "%%ct_" as these are reserved for the WPSEO standard variable variables for custom fields and custom taxonomies. Try making your variable name unique.', 'wordpress-seo' ), E_USER_WARNING ); 
  104. elseif ( ! method_exists( __CLASS__, 'retrieve_' . $var ) ) { 
  105. if ( ! isset( self::$external_replacements[ $var ] ) ) { 
  106. self::$external_replacements[ $var ] = $replace_function; 
  107. self::register_help_text( $type, $var, $help_text ); 
  108. $success = true; 
  109. else { 
  110. trigger_error( __( 'A replacement variable with the same name has already been registered. Try making your variable name unique.', 'wordpress-seo' ), E_USER_WARNING ); 
  111. else { 
  112. trigger_error( __( 'You cannot overrule a WPSEO standard variable replacement by registering a variable with the same name. Use the "wpseo_replacements" filter instead to adjust the replacement value.', 'wordpress-seo' ), E_USER_WARNING ); 
  113.  
  114. return $success; 
  115.  
  116.  
  117. /** 
  118. * Replace `%%variable_placeholders%%` with their real value based on the current requested page/post/cpt/etc 
  119. * 
  120. * @param string $string the string to replace the variables in. 
  121. * @param array $args the object some of the replacement values might come from,  
  122. * could be a post, taxonomy or term. 
  123. * @param array $omit variables that should not be replaced by this function. 
  124. * 
  125. * @return string 
  126. */ 
  127. public function replace( $string, $args, $omit = array() ) { 
  128.  
  129. $string = strip_tags( $string ); 
  130.  
  131. // Let's see if we can bail super early. 
  132. if ( strpos( $string, '%%' ) === false ) { 
  133. return WPSEO_Utils::standardize_whitespace( $string ); 
  134.  
  135. $args = (array) $args; 
  136. if ( isset( $args['post_content'] ) && ! empty( $args['post_content'] ) ) { 
  137. $args['post_content'] = WPSEO_Utils::strip_shortcode( $args['post_content'] ); 
  138. if ( isset( $args['post_excerpt'] ) && ! empty( $args['post_excerpt'] ) ) { 
  139. $args['post_excerpt'] = WPSEO_Utils::strip_shortcode( $args['post_excerpt'] ); 
  140. $this->args = (object) wp_parse_args( $args, $this->defaults ); 
  141.  
  142. // Clean $omit array. 
  143. if ( is_array( $omit ) && $omit !== array() ) { 
  144. $omit = array_map( array( __CLASS__, 'remove_var_delimiter' ), $omit ); 
  145.  
  146. $replacements = array(); 
  147. if ( preg_match_all( '`%%([^%]+(%%single)?)%%?`iu', $string, $matches ) ) { 
  148. $replacements = $this->set_up_replacements( $matches, $omit ); 
  149.  
  150. /** 
  151. * Filter: 'wpseo_replacements' - Allow customization of the replacements before they are applied 
  152. * 
  153. * @api array $replacements The replacements 
  154. */ 
  155. $replacements = apply_filters( 'wpseo_replacements', $replacements ); 
  156.  
  157. // Do the actual replacements. 
  158. if ( is_array( $replacements ) && $replacements !== array() ) { 
  159. $string = str_replace( array_keys( $replacements ), array_values( $replacements ), $string ); 
  160.  
  161. /** 
  162. * Filter: 'wpseo_replacements_final' - Allow overruling of whether or not to remove placeholders 
  163. * which didn't yield a replacement 
  164. * 
  165. * @example <code>add_filter( 'wpseo_replacements_final', '__return_false' );</code> 
  166. * 
  167. * @api bool $final 
  168. */ 
  169. if ( apply_filters( 'wpseo_replacements_final', true ) === true && ( isset( $matches[1] ) && is_array( $matches[1] ) ) ) { 
  170. // Remove non-replaced variables. 
  171. $remove = array_diff( $matches[1], $omit ); // Make sure the $omit variables do not get removed. 
  172. $remove = array_map( array( __CLASS__, 'add_var_delimiter' ), $remove ); 
  173. $string = str_replace( $remove, '', $string ); 
  174.  
  175. // Undouble separators which have nothing between them, i.e. where a non-replaced variable was removed. 
  176. if ( isset( $replacements['%%sep%%'] ) && ( is_string( $replacements['%%sep%%'] ) && $replacements['%%sep%%'] !== '' ) ) { 
  177. $q_sep = preg_quote( $replacements['%%sep%%'], '`' ); 
  178. $string = preg_replace( '`' . $q_sep . '(?:\s*' . $q_sep . ')*`u', $replacements['%%sep%%'], $string ); 
  179.  
  180. // Remove superfluous whitespace. 
  181. $string = WPSEO_Utils::standardize_whitespace( $string ); 
  182.  
  183. return trim( $string ); 
  184.  
  185.  
  186. /** 
  187. * Retrieve the replacements for the variables found. 
  188. * 
  189. * @param array $matches variables found in the original string - regex result. 
  190. * @param array $omit variables that should not be replaced by this function. 
  191. * 
  192. * @return array retrieved replacements - this might be a smaller array as some variables 
  193. * may not yield a replacement in certain contexts. 
  194. */ 
  195. private function set_up_replacements( $matches, $omit ) { 
  196.  
  197. $replacements = array(); 
  198.  
  199. // @todo -> figure out a way to deal with external functions starting with cf_/ct_. 
  200. foreach ( $matches[1] as $k => $var ) { 
  201.  
  202. // Don't set up replacements which should be omitted. 
  203. if ( in_array( $var, $omit, true ) ) { 
  204. continue; 
  205.  
  206. // Deal with variable variable names first. 
  207. if ( strpos( $var, 'cf_' ) === 0 ) { 
  208. $replacement = $this->retrieve_cf_custom_field_name( $var ); 
  209. elseif ( strpos( $var, 'ct_desc_' ) === 0 ) { 
  210. $replacement = $this->retrieve_ct_desc_custom_tax_name( $var ); 
  211. elseif ( strpos( $var, 'ct_' ) === 0 ) { 
  212. $single = ( isset( $matches[2][ $k ] ) && $matches[2][ $k ] !== '' ) ? true : false; 
  213. $replacement = $this->retrieve_ct_custom_tax_name( $var, $single ); 
  214. } // Deal with non-variable variable names. 
  215. elseif ( method_exists( $this, 'retrieve_' . $var ) ) { 
  216. $method_name = 'retrieve_' . $var; 
  217. $replacement = $this->$method_name(); 
  218. } // Deal with externally defined variable names. 
  219. elseif ( isset( self::$external_replacements[ $var ] ) && ! is_null( self::$external_replacements[ $var ] ) ) { 
  220. $replacement = call_user_func( self::$external_replacements[ $var ], $var, $this->args ); 
  221.  
  222. // Replacement retrievals can return null if no replacement can be determined, root those outs. 
  223. if ( isset( $replacement ) ) { 
  224. $var = self::add_var_delimiter( $var ); 
  225. $replacements[ $var ] = $replacement; 
  226. unset( $replacement, $single, $method_name ); 
  227.  
  228. return $replacements; 
  229.  
  230.  
  231.  
  232. /** *********************** BASIC VARIABLES ************************** */ 
  233.  
  234. /** 
  235. * Retrieve the post/cpt categories (comma separated) for use as replacement string. 
  236. * 
  237. * @return string|null 
  238. */ 
  239. private function retrieve_category() { 
  240. $replacement = null; 
  241.  
  242. if ( ! empty( $this->args->ID ) ) { 
  243. $cat = $this->get_terms( $this->args->ID, 'category' ); 
  244. if ( $cat !== '' ) { 
  245. $replacement = $cat; 
  246.  
  247. if ( ( ! isset( $replacement ) || $replacement === '' ) && ( isset( $this->args->cat_name ) && ! empty( $this->args->cat_name ) ) ) { 
  248. $replacement = $this->args->cat_name; 
  249.  
  250. return $replacement; 
  251.  
  252. /** 
  253. * Retrieve the category description for use as replacement string. 
  254. * 
  255. * @return string|null 
  256. */ 
  257. private function retrieve_category_description() { 
  258. return $this->retrieve_term_description(); 
  259.  
  260. /** 
  261. * Retrieve the date of the post/page/cpt for use as replacement string. 
  262. * 
  263. * @return string|null 
  264. */ 
  265. private function retrieve_date() { 
  266. $replacement = null; 
  267.  
  268. if ( $this->args->post_date !== '' ) { 
  269. $replacement = mysql2date( get_option( 'date_format' ), $this->args->post_date, true ); 
  270. else { 
  271. if ( get_query_var( 'day' ) && get_query_var( 'day' ) !== '' ) { 
  272. $replacement = get_the_date(); 
  273. else { 
  274. if ( single_month_title( ' ', false ) && single_month_title( ' ', false ) !== '' ) { 
  275. $replacement = single_month_title( ' ', false ); 
  276. elseif ( get_query_var( 'year' ) !== '' ) { 
  277. $replacement = get_query_var( 'year' ); 
  278.  
  279. return $replacement; 
  280.  
  281. /** 
  282. * Retrieve the post/page/cpt excerpt for use as replacement string. 
  283. * The excerpt will be auto-generated if it does not exist. 
  284. * 
  285. * @return string|null 
  286. */ 
  287. private function retrieve_excerpt() { 
  288. $replacement = null; 
  289.  
  290. if ( ! empty( $this->args->ID ) ) { 
  291. if ( $this->args->post_excerpt !== '' ) { 
  292. $replacement = strip_tags( $this->args->post_excerpt ); 
  293. elseif ( $this->args->post_content !== '' ) { 
  294. $replacement = wp_html_excerpt( strip_shortcodes( $this->args->post_content ), 155 ); 
  295.  
  296. return $replacement; 
  297.  
  298. /** 
  299. * Retrieve the post/page/cpt excerpt for use as replacement string (without auto-generation). 
  300. * 
  301. * @return string|null 
  302. */ 
  303. private function retrieve_excerpt_only() { 
  304. $replacement = null; 
  305.  
  306. if ( ! empty( $this->args->ID ) && $this->args->post_excerpt !== '' ) { 
  307. $replacement = strip_tags( $this->args->post_excerpt ); 
  308.  
  309. return $replacement; 
  310.  
  311. /** 
  312. * Retrieve the title of the parent page of the current page/cpt for use as replacement string. 
  313. * Only applicable for hierarchical post types. 
  314. * 
  315. * @todo - check: shouldn't this use $this->args as well ? 
  316. * 
  317. * @return string|null 
  318. */ 
  319. private function retrieve_parent_title() { 
  320. $replacement = null; 
  321.  
  322. if ( ! isset( $replacement ) && ( ( is_singular() || is_admin() ) && isset( $GLOBALS['post'] ) ) ) { 
  323. if ( isset( $GLOBALS['post']->post_parent ) && 0 !== $GLOBALS['post']->post_parent ) { 
  324. $replacement = get_the_title( $GLOBALS['post']->post_parent ); 
  325.  
  326. return $replacement; 
  327.  
  328. /** 
  329. * Retrieve the current search phrase for use as replacement string. 
  330. * 
  331. * @return string|null 
  332. */ 
  333. private function retrieve_searchphrase() { 
  334. $replacement = null; 
  335.  
  336. if ( ! isset( $replacement ) ) { 
  337. $search = get_query_var( 's' ); 
  338. if ( $search !== '' ) { 
  339. $replacement = esc_html( $search ); 
  340.  
  341. return $replacement; 
  342.  
  343. /** 
  344. * Retrieve the separator for use as replacement string. 
  345. * 
  346. * @return string 
  347. */ 
  348. private function retrieve_sep() { 
  349. return WPSEO_Utils::get_title_separator(); 
  350.  
  351. /** 
  352. * Retrieve the site's tag line / description for use as replacement string. 
  353. * 
  354. * @return string|null 
  355. */ 
  356. private function retrieve_sitedesc() { 
  357. static $replacement; 
  358.  
  359. if ( ! isset( $replacement ) ) { 
  360. $description = trim( strip_tags( get_bloginfo( 'description' ) ) ); 
  361. if ( $description !== '' ) { 
  362. $replacement = $description; 
  363.  
  364. return $replacement; 
  365.  
  366.  
  367. /** 
  368. * Retrieve the site's name for use as replacement string. 
  369. * 
  370. * @return string|null 
  371. */ 
  372. private function retrieve_sitename() { 
  373. static $replacement; 
  374.  
  375. if ( ! isset( $replacement ) ) { 
  376. $sitename = WPSEO_Utils::get_site_name(); 
  377. if ( $sitename !== '' ) { 
  378. $replacement = $sitename; 
  379.  
  380. return $replacement; 
  381.  
  382. /** 
  383. * Retrieve the current tag/tags for use as replacement string. 
  384. * 
  385. * @return string|null 
  386. */ 
  387. private function retrieve_tag() { 
  388. $replacement = null; 
  389.  
  390. if ( isset( $this->args->ID ) ) { 
  391. $tags = $this->get_terms( $this->args->ID, 'post_tag' ); 
  392. if ( $tags !== '' ) { 
  393. $replacement = $tags; 
  394.  
  395. return $replacement; 
  396.  
  397. /** 
  398. * Retrieve the tag description for use as replacement string. 
  399. * 
  400. * @return string|null 
  401. */ 
  402. private function retrieve_tag_description() { 
  403. return $this->retrieve_term_description(); 
  404.  
  405. /** 
  406. * Retrieve the term description for use as replacement string. 
  407. * 
  408. * @return string|null 
  409. */ 
  410. private function retrieve_term_description() { 
  411. $replacement = null; 
  412.  
  413. if ( isset( $this->args->term_id ) && ! empty( $this->args->taxonomy ) ) { 
  414. $term_desc = get_term_field( 'description', $this->args->term_id, $this->args->taxonomy ); 
  415. if ( $term_desc !== '' ) { 
  416. $replacement = trim( strip_tags( $term_desc ) ); 
  417.  
  418. return $replacement; 
  419.  
  420. /** 
  421. * Retrieve the term name for use as replacement string. 
  422. * 
  423. * @return string|null 
  424. */ 
  425. private function retrieve_term_title() { 
  426. $replacement = null; 
  427.  
  428. if ( ! empty( $this->args->taxonomy ) && ! empty( $this->args->name ) ) { 
  429. $replacement = $this->args->name; 
  430.  
  431. return $replacement; 
  432.  
  433. /** 
  434. * Retrieve the title of the post/page/cpt for use as replacement string. 
  435. * 
  436. * @return string|null 
  437. */ 
  438. private function retrieve_title() { 
  439. $replacement = null; 
  440.  
  441. if ( is_string( $this->args->post_title ) && $this->args->post_title !== '' ) { 
  442. $replacement = stripslashes( $this->args->post_title ); 
  443.  
  444. return $replacement; 
  445.  
  446. /** 
  447. * Retrieve primary category for use as replacement string. 
  448. * 
  449. * @return bool|int|null 
  450. */ 
  451. private function retrieve_primary_category() { 
  452. $primary_category = null; 
  453.  
  454. if ( ! empty( $this->args->ID ) ) { 
  455. $wpseo_primary_category = new WPSEO_Primary_Term( 'category', $this->args->ID ); 
  456.  
  457. $term_id = $wpseo_primary_category->get_primary_term(); 
  458. $term = get_term( $term_id ); 
  459.  
  460. if ( ! is_wp_error( $term ) && ! empty( $term ) ) { 
  461. $primary_category = $term->name; 
  462.  
  463. return $primary_category; 
  464.  
  465.  
  466. /** *********************** ADVANCED VARIABLES ************************** */ 
  467.  
  468. /** 
  469. * Determine the page numbering of the current post/page/cpt 
  470. * 
  471. * @param string $request 'nr'|'max' - whether to return the page number or the max number of pages. 
  472. * 
  473. * @return int|null 
  474. */ 
  475. private function determine_pagenumbering( $request = 'nr' ) { 
  476. global $wp_query, $post; 
  477. $max_num_pages = null; 
  478. $page_number = null; 
  479.  
  480. $max_num_pages = 1; 
  481.  
  482. if ( ! is_singular() ) { 
  483. $page_number = get_query_var( 'paged' ); 
  484. if ( $page_number === 0 || $page_number === '' ) { 
  485. $page_number = 1; 
  486.  
  487. if ( isset( $wp_query->max_num_pages ) && ( $wp_query->max_num_pages != '' && $wp_query->max_num_pages != 0 ) ) { 
  488. $max_num_pages = $wp_query->max_num_pages; 
  489. else { 
  490. $page_number = get_query_var( 'page' ); 
  491. if ( $page_number === 0 || $page_number === '' ) { 
  492. $page_number = 1; 
  493.  
  494. if ( isset( $post->post_content ) ) { 
  495. $max_num_pages = ( substr_count( $post->post_content, '<!--nextpage-->' ) + 1 ); 
  496.  
  497. $return = null; 
  498.  
  499. switch ( $request ) { 
  500. case 'nr': 
  501. $return = $page_number; 
  502. break; 
  503. case 'max': 
  504. $return = $max_num_pages; 
  505. break; 
  506.  
  507. return $return; 
  508.  
  509.  
  510. /** 
  511. * Determine the post type names for the current post/page/cpt 
  512. * 
  513. * @param string $request 'single'|'plural' - whether to return the single or plural form. 
  514. * 
  515. * @return string|null 
  516. */ 
  517. private function determine_pt_names( $request = 'single' ) { 
  518. global $wp_query; 
  519. $pt_single = null; 
  520. $pt_plural = null; 
  521.  
  522. if ( isset( $wp_query->query_vars['post_type'] ) && ( ( is_string( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] !== '' ) || ( is_array( $wp_query->query_vars['post_type'] ) && $wp_query->query_vars['post_type'] !== array() ) ) ) { 
  523. $post_type = $wp_query->query_vars['post_type']; 
  524. elseif ( isset( $this->args->post_type ) && ( is_string( $this->args->post_type ) && $this->args->post_type !== '' ) ) { 
  525. $post_type = $this->args->post_type; 
  526. else { 
  527. // Make it work in preview mode. 
  528. $post_type = $wp_query->get_queried_object()->post_type; 
  529.  
  530. if ( is_array( $post_type ) ) { 
  531. $post_type = reset( $post_type ); 
  532.  
  533. if ( $post_type !== '' ) { 
  534. $pt = get_post_type_object( $post_type ); 
  535. $pt_plural = $pt_single = $pt->name; 
  536. if ( isset( $pt->labels->singular_name ) ) { 
  537. $pt_single = $pt->labels->singular_name; 
  538. if ( isset( $pt->labels->name ) ) { 
  539. $pt_plural = $pt->labels->name; 
  540.  
  541. $return = null; 
  542.  
  543. switch ( $request ) { 
  544. case 'single': 
  545. $return = $pt_single; 
  546. break; 
  547. case 'plural': 
  548. $return = $pt_plural; 
  549. break; 
  550.  
  551. return $return; 
  552.  
  553. /** 
  554. * Retrieve the attachment caption for use as replacement string. 
  555. * 
  556. * @return string|null 
  557. */ 
  558. private function retrieve_caption() { 
  559. return $this->retrieve_excerpt_only(); 
  560.  
  561.  
  562. /** 
  563. * Retrieve a post/page/cpt's custom field value for use as replacement string 
  564. * 
  565. * @param string $var The complete variable to replace which includes the name of 
  566. * the custom field which value is to be retrieved. 
  567. * 
  568. * @return string|null 
  569. */ 
  570. private function retrieve_cf_custom_field_name( $var ) { 
  571. global $post; 
  572. $replacement = null; 
  573.  
  574. if ( is_string( $var ) && $var !== '' ) { 
  575. $field = substr( $var, 3 ); 
  576. if ( ( is_singular() || is_admin() ) && ( is_object( $post ) && isset( $post->ID ) ) ) { 
  577. $name = get_post_meta( $post->ID, $field, true ); 
  578. if ( $name !== '' ) { 
  579. $replacement = $name; 
  580.  
  581. return $replacement; 
  582.  
  583.  
  584. /** 
  585. * Retrieve a post/page/cpt's custom taxonomies for use as replacement string 
  586. * 
  587. * @param string $var The complete variable to replace which includes the name of 
  588. * the custom taxonomy which value(s) is to be retrieved. 
  589. * @param bool $single Whether to retrieve only the first or all values for the taxonomy. 
  590. * 
  591. * @return string|null 
  592. */ 
  593. private function retrieve_ct_custom_tax_name( $var, $single = false ) { 
  594. $replacement = null; 
  595.  
  596. if ( ( is_string( $var ) && $var !== '' ) && ! empty( $this->args->ID ) ) { 
  597. $tax = substr( $var, 3 ); 
  598. $name = $this->get_terms( $this->args->ID, $tax, $single ); 
  599. if ( $name !== '' ) { 
  600. $replacement = $name; 
  601.  
  602. return $replacement; 
  603.  
  604.  
  605. /** 
  606. * Retrieve a post/page/cpt's custom taxonomies description for use as replacement string 
  607. * 
  608. * @param string $var The complete variable to replace which includes the name of 
  609. * the custom taxonomy which description is to be retrieved. 
  610. * 
  611. * @return string|null 
  612. */ 
  613. private function retrieve_ct_desc_custom_tax_name( $var ) { 
  614. global $post; 
  615. $replacement = null; 
  616.  
  617. if ( is_string( $var ) && $var !== '' ) { 
  618. $tax = substr( $var, 8 ); 
  619. if ( is_object( $post ) && isset( $post->ID ) ) { 
  620. $terms = get_the_terms( $post->ID, $tax ); 
  621. if ( is_array( $terms ) && $terms !== array() ) { 
  622. $term = current( $terms ); 
  623. $term_desc = get_term_field( 'description', $term->term_id, $tax ); 
  624. if ( $term_desc !== '' ) { 
  625. $replacement = trim( strip_tags( $term_desc ) ); 
  626.  
  627. return $replacement; 
  628.  
  629. /** 
  630. * Retrieve the current date for use as replacement string. 
  631. * 
  632. * @return string 
  633. */ 
  634. private function retrieve_currentdate() { 
  635. static $replacement; 
  636.  
  637. if ( ! isset( $replacement ) ) { 
  638. $replacement = date_i18n( get_option( 'date_format' ) ); 
  639.  
  640. return $replacement; 
  641.  
  642. /** 
  643. * Retrieve the current day for use as replacement string. 
  644. * 
  645. * @return string 
  646. */ 
  647. private function retrieve_currentday() { 
  648. static $replacement; 
  649.  
  650. if ( ! isset( $replacement ) ) { 
  651. $replacement = date_i18n( 'j' ); 
  652.  
  653. return $replacement; 
  654.  
  655. /** 
  656. * Retrieve the current month for use as replacement string. 
  657. * 
  658. * @return string 
  659. */ 
  660. private function retrieve_currentmonth() { 
  661. static $replacement; 
  662.  
  663. if ( ! isset( $replacement ) ) { 
  664. $replacement = date_i18n( 'F' ); 
  665.  
  666. return $replacement; 
  667.  
  668. /** 
  669. * Retrieve the current time for use as replacement string. 
  670. * 
  671. * @return string 
  672. */ 
  673. private function retrieve_currenttime() { 
  674. static $replacement; 
  675.  
  676. if ( ! isset( $replacement ) ) { 
  677. $replacement = date_i18n( get_option( 'time_format' ) ); 
  678.  
  679. return $replacement; 
  680.  
  681. /** 
  682. * Retrieve the current year for use as replacement string. 
  683. * 
  684. * @return string 
  685. */ 
  686. private function retrieve_currentyear() { 
  687. static $replacement; 
  688.  
  689. if ( ! isset( $replacement ) ) { 
  690. $replacement = date_i18n( 'Y' ); 
  691.  
  692. return $replacement; 
  693.  
  694. /** 
  695. * Retrieve the post/page/cpt's focus keyword for use as replacement string. 
  696. * 
  697. * @return string|null 
  698. */ 
  699. private function retrieve_focuskw() { 
  700. $replacement = null; 
  701.  
  702. if ( ! empty( $this->args->ID ) ) { 
  703. $focus_kw = WPSEO_Meta::get_value( 'focuskw', $this->args->ID ); 
  704. if ( $focus_kw !== '' ) { 
  705. $replacement = $focus_kw; 
  706.  
  707. return $replacement; 
  708.  
  709. /** 
  710. * Retrieve the post/page/cpt ID for use as replacement string. 
  711. * 
  712. * @return string|null 
  713. */ 
  714. private function retrieve_id() { 
  715. $replacement = null; 
  716.  
  717. if ( ! empty( $this->args->ID ) ) { 
  718. $replacement = $this->args->ID; 
  719.  
  720. return $replacement; 
  721.  
  722. /** 
  723. * Retrieve the post/page/cpt modified time for use as replacement string. 
  724. * 
  725. * @return string|null 
  726. */ 
  727. private function retrieve_modified() { 
  728. $replacement = null; 
  729.  
  730. if ( ! empty( $this->args->post_modified ) ) { 
  731. $replacement = mysql2date( get_option( 'date_format' ), $this->args->post_modified, true ); 
  732.  
  733. return $replacement; 
  734.  
  735. /** 
  736. * Retrieve the post/page/cpt author's "nice name" for use as replacement string. 
  737. * 
  738. * @return string|null 
  739. */ 
  740. private function retrieve_name() { 
  741. $replacement = null; 
  742.  
  743. $user_id = $this->retrieve_userid(); 
  744. $name = get_the_author_meta( 'display_name', $user_id ); 
  745. if ( $name !== '' ) { 
  746. $replacement = $name; 
  747.  
  748. return $replacement; 
  749.  
  750. /** 
  751. * Retrieve the post/page/cpt author's users description for use as a replacement string. 
  752. * 
  753. * @return null|string 
  754. */ 
  755. private function retrieve_user_description() { 
  756. $replacement = null; 
  757.  
  758. $user_id = $this->retrieve_userid(); 
  759. $description = get_the_author_meta( 'description', $user_id ); 
  760. if ( $description != '' ) { 
  761. $replacement = $description; 
  762.  
  763. return $replacement; 
  764.  
  765. /** 
  766. * Retrieve the current page number with context (i.e. 'page 2 of 4') for use as replacement string. 
  767. * 
  768. * @return string 
  769. */ 
  770. private function retrieve_page() { 
  771. $replacement = null; 
  772.  
  773. $max = $this->determine_pagenumbering( 'max' ); 
  774. $nr = $this->determine_pagenumbering( 'nr' ); 
  775. $sep = $this->retrieve_sep(); 
  776.  
  777. if ( $max > 1 && $nr > 1 ) { 
  778. $replacement = sprintf( $sep . ' ' . __( 'Page %1$d of %2$d', 'wordpress-seo' ), $nr, $max ); 
  779.  
  780. return $replacement; 
  781.  
  782. /** 
  783. * Retrieve the current page number for use as replacement string. 
  784. * 
  785. * @return string|null 
  786. */ 
  787. private function retrieve_pagenumber() { 
  788. $replacement = null; 
  789.  
  790. $nr = $this->determine_pagenumbering( 'nr' ); 
  791. if ( isset( $nr ) && $nr > 0 ) { 
  792. $replacement = (string) $nr; 
  793.  
  794. return $replacement; 
  795.  
  796. /** 
  797. * Retrieve the current page total for use as replacement string. 
  798. * 
  799. * @return string|null 
  800. */ 
  801. private function retrieve_pagetotal() { 
  802. $replacement = null; 
  803.  
  804. $max = $this->determine_pagenumbering( 'max' ); 
  805. if ( isset( $max ) && $max > 0 ) { 
  806. $replacement = (string) $max; 
  807.  
  808. return $replacement; 
  809.  
  810. /** 
  811. * Retrieve the post type plural label for use as replacement string. 
  812. * 
  813. * @return string|null 
  814. */ 
  815. private function retrieve_pt_plural() { 
  816. $replacement = null; 
  817.  
  818. $name = $this->determine_pt_names( 'plural' ); 
  819. if ( isset( $name ) && $name !== '' ) { 
  820. $replacement = $name; 
  821.  
  822. return $replacement; 
  823.  
  824. /** 
  825. * Retrieve the post type single label for use as replacement string. 
  826. * 
  827. * @return string|null 
  828. */ 
  829. private function retrieve_pt_single() { 
  830. $replacement = null; 
  831.  
  832. $name = $this->determine_pt_names( 'single' ); 
  833. if ( isset( $name ) && $name !== '' ) { 
  834. $replacement = $name; 
  835.  
  836. return $replacement; 
  837.  
  838. /** 
  839. * Retrieve the slug which caused the 404 for use as replacement string. 
  840. * 
  841. * @return string|null 
  842. */ 
  843. private function retrieve_term404() { 
  844. $replacement = null; 
  845.  
  846. if ( $this->args->term404 !== '' ) { 
  847. $replacement = sanitize_text_field( str_replace( '-', ' ', $this->args->term404 ) ); 
  848. else { 
  849. $error_request = get_query_var( 'pagename' ); 
  850. if ( $error_request !== '' ) { 
  851. $replacement = sanitize_text_field( str_replace( '-', ' ', $error_request ) ); 
  852. else { 
  853. $error_request = get_query_var( 'name' ); 
  854. if ( $error_request !== '' ) { 
  855. $replacement = sanitize_text_field( str_replace( '-', ' ', $error_request ) ); 
  856.  
  857. return $replacement; 
  858.  
  859. /** 
  860. * Retrieve the post/page/cpt author's user id for use as replacement string. 
  861. * 
  862. * @return string 
  863. */ 
  864. private function retrieve_userid() { 
  865. $replacement = ! empty( $this->args->post_author ) ? $this->args->post_author : get_query_var( 'author' ); 
  866.  
  867. return $replacement; 
  868.  
  869.  
  870.  
  871. /** *********************** HELP TEXT RELATED ************************** */ 
  872.  
  873. /** 
  874. * Create a variable help text table 
  875. * 
  876. * @param string $type Either 'basic' or 'advanced'. 
  877. * 
  878. * @return string Help text table 
  879. */ 
  880. private static function create_variable_help_table( $type ) { 
  881. if ( ! in_array( $type, array( 'basic', 'advanced' ), true ) ) { 
  882. return ''; 
  883.  
  884. $table = ' 
  885. <table class="yoast_help yoast-table-scrollable"> 
  886. <thead> 
  887. <tr> 
  888. <th scope="col">' . esc_html__( 'Variable', 'wordpress-seo' ) . '</th> 
  889. <th scope="col">' . esc_html__( 'Description', 'wordpress-seo' ) . '</th> 
  890. </tr> 
  891. </thead> 
  892. <tbody>'; 
  893.  
  894. foreach ( self::$help_texts[ $type ] as $replace => $help_text ) { 
  895. $table .= ' 
  896. <tr> 
  897. <td class="yoast-variable-name">%%' . esc_html( $replace ) . '%%</td> 
  898. <td class="yoast-variable-desc">' . $help_text . '</td> 
  899. </tr>'; 
  900.  
  901. $table .= ' 
  902. </tbody> 
  903. </table>'; 
  904.  
  905. return $table; 
  906.  
  907. /** 
  908. * Create the help text table for the basic variables for use in a help tab 
  909. * 
  910. * @return string 
  911. */ 
  912. public static function get_basic_help_texts() { 
  913. return self::create_variable_help_table( 'basic' ); 
  914.  
  915.  
  916. /** 
  917. * Create the help text table for the advanced variables for use in a help tab 
  918. * 
  919. * @return string 
  920. */ 
  921. public static function get_advanced_help_texts() { 
  922. return self::create_variable_help_table( 'advanced' ); 
  923.  
  924.  
  925. /** 
  926. * Set the help text for a user/plugin/theme defined extra variable. 
  927. * 
  928. * @param string $type Type of variable: 'basic' or 'advanced'. 
  929. * @param string $replace Variable to replace, i.e. '%%var%%'. 
  930. * @param string $help_text The actual help text string. 
  931. */ 
  932. private static function register_help_text( $type, $replace, $help_text = '' ) { 
  933. if ( is_string( $replace ) && $replace !== '' ) { 
  934. $replace = self::remove_var_delimiter( $replace ); 
  935.  
  936. if ( ( is_string( $type ) && in_array( $type, array( 
  937. 'basic',  
  938. 'advanced',  
  939. ), true ) ) && ( $replace !== '' && ! isset( self::$help_texts[ $type ][ $replace ] ) ) 
  940. ) { 
  941. self::$help_texts[ $type ][ $replace ] = $help_text; 
  942.  
  943.  
  944. /** 
  945. * Set/translate the help texts for the WPSEO standard basic variables. 
  946. */ 
  947. private static function set_basic_help_texts() { 
  948. self::$help_texts['basic'] = array( 
  949. 'date' => __( 'Replaced with the date of the post/page', 'wordpress-seo' ),  
  950. 'title' => __( 'Replaced with the title of the post/page', 'wordpress-seo' ),  
  951. 'parent_title' => __( 'Replaced with the title of the parent page of the current page', 'wordpress-seo' ),  
  952. 'sitename' => __( 'The site\'s name', 'wordpress-seo' ),  
  953. 'sitedesc' => __( 'The site\'s tag line / description', 'wordpress-seo' ),  
  954. 'excerpt' => __( 'Replaced with the post/page excerpt (or auto-generated if it does not exist)', 'wordpress-seo' ),  
  955. 'excerpt_only' => __( 'Replaced with the post/page excerpt (without auto-generation)', 'wordpress-seo' ),  
  956. 'tag' => __( 'Replaced with the current tag/tags', 'wordpress-seo' ),  
  957. 'category' => __( 'Replaced with the post categories (comma separated)', 'wordpress-seo' ),  
  958. 'primary_category' => __( 'Replaced with the primary category of the post/page', 'wordpress-seo' ),  
  959. 'category_description' => __( 'Replaced with the category description', 'wordpress-seo' ),  
  960. 'tag_description' => __( 'Replaced with the tag description', 'wordpress-seo' ),  
  961. 'term_description' => __( 'Replaced with the term description', 'wordpress-seo' ),  
  962. 'term_title' => __( 'Replaced with the term name', 'wordpress-seo' ),  
  963. 'searchphrase' => __( 'Replaced with the current search phrase', 'wordpress-seo' ),  
  964. 'sep' => sprintf( 
  965. /** translators: %s: wp_title() function */ 
  966. __( 'The separator defined in your theme\'s %s tag.', 'wordpress-seo' ),  
  967. '<code>wp_title()</code>' 
  968. ),  
  969. ); 
  970.  
  971. /** 
  972. * Set/translate the help texts for the WPSEO standard advanced variables. 
  973. */ 
  974. private static function set_advanced_help_texts() { 
  975. self::$help_texts['advanced'] = array( 
  976. 'pt_single' => __( 'Replaced with the post type single label', 'wordpress-seo' ),  
  977. 'pt_plural' => __( 'Replaced with the post type plural label', 'wordpress-seo' ),  
  978. 'modified' => __( 'Replaced with the post/page modified time', 'wordpress-seo' ),  
  979. 'id' => __( 'Replaced with the post/page ID', 'wordpress-seo' ),  
  980. 'name' => __( 'Replaced with the post/page author\'s \'nicename\'', 'wordpress-seo' ),  
  981. 'user_description' => __( 'Replaced with the post/page author\'s \'Biographical Info\'', 'wordpress-seo' ),  
  982. 'userid' => __( 'Replaced with the post/page author\'s userid', 'wordpress-seo' ),  
  983. 'currenttime' => __( 'Replaced with the current time', 'wordpress-seo' ),  
  984. 'currentdate' => __( 'Replaced with the current date', 'wordpress-seo' ),  
  985. 'currentday' => __( 'Replaced with the current day', 'wordpress-seo' ),  
  986. 'currentmonth' => __( 'Replaced with the current month', 'wordpress-seo' ),  
  987. 'currentyear' => __( 'Replaced with the current year', 'wordpress-seo' ),  
  988. 'page' => __( 'Replaced with the current page number with context (i.e. page 2 of 4)', 'wordpress-seo' ),  
  989. 'pagetotal' => __( 'Replaced with the current page total', 'wordpress-seo' ),  
  990. 'pagenumber' => __( 'Replaced with the current page number', 'wordpress-seo' ),  
  991. 'caption' => __( 'Attachment caption', 'wordpress-seo' ),  
  992. 'focuskw' => __( 'Replaced with the posts focus keyword', 'wordpress-seo' ),  
  993. 'term404' => __( 'Replaced with the slug which caused the 404', 'wordpress-seo' ),  
  994. 'cf_<custom-field-name>' => __( 'Replaced with a posts custom field value', 'wordpress-seo' ),  
  995. 'ct_<custom-tax-name>' => __( 'Replaced with a posts custom taxonomies, comma separated.', 'wordpress-seo' ),  
  996. 'ct_desc_<custom-tax-name>' => __( 'Replaced with a custom taxonomies description', 'wordpress-seo' ),  
  997. ); 
  998.  
  999.  
  1000.  
  1001.  
  1002. /** *********************** GENERAL HELPER METHODS ************************** */ 
  1003.  
  1004. /** 
  1005. * Remove the '%%' delimiters from a variable string 
  1006. * 
  1007. * @param string $string Variable string to be cleaned. 
  1008. * 
  1009. * @return string 
  1010. */ 
  1011. private static function remove_var_delimiter( $string ) { 
  1012. return trim( $string, '%' ); 
  1013.  
  1014. /** 
  1015. * Add the '%%' delimiters to a variable string 
  1016. * 
  1017. * @param string $string Variable string to be delimited. 
  1018. * 
  1019. * @return string 
  1020. */ 
  1021. private static function add_var_delimiter( $string ) { 
  1022. return '%%' . $string . '%%'; 
  1023.  
  1024. /** 
  1025. * Retrieve a post's terms, comma delimited. 
  1026. * 
  1027. * @param int $id ID of the post to get the terms for. 
  1028. * @param string $taxonomy The taxonomy to get the terms for this post from. 
  1029. * @param bool $return_single If true, return the first term. 
  1030. * 
  1031. * @return string either a single term or a comma delimited string of terms. 
  1032. */ 
  1033. public function get_terms( $id, $taxonomy, $return_single = false ) { 
  1034.  
  1035. $output = ''; 
  1036.  
  1037. // If we're on a specific tag, category or taxonomy page, use that. 
  1038. if ( is_category() || is_tag() || is_tax() ) { 
  1039. $term = $GLOBALS['wp_query']->get_queried_object(); 
  1040. $output = $term->name; 
  1041. elseif ( ! empty( $id ) && ! empty( $taxonomy ) ) { 
  1042. $terms = get_the_terms( $id, $taxonomy ); 
  1043. if ( is_array( $terms ) && $terms !== array() ) { 
  1044. foreach ( $terms as $term ) { 
  1045. if ( $return_single ) { 
  1046. $output = $term->name; 
  1047. break; 
  1048. else { 
  1049. $output .= $term->name . ', '; 
  1050. $output = rtrim( trim( $output ), ', ' ); 
  1051. unset( $terms, $term ); 
  1052.  
  1053. /** 
  1054. * Allows filtering of the terms list used to replace %%category%%, %%tag%% and %%ct_<custom-tax-name>%% variables 
  1055. * 
  1056. * @api string $output Comma-delimited string containing the terms 
  1057. */ 
  1058. return apply_filters( 'wpseo_terms', $output ); 
  1059. } /** End of class WPSEO_Replace_Vars */ 
  1060.  
  1061.  
  1062. /** 
  1063. * Setup the class statics when the file is first loaded 
  1064. */ 
  1065. WPSEO_Replace_Vars::setup_statics_once(); 
.