| Server IP : 80.74.154.100 / Your IP : 216.73.217.0 Web Server : Apache System : Linux marissa.metanet.ch 4.18.0-553.141.2.lve.el7h.x86_64 #1 SMP Wed Jul 8 17:20:31 UTC 2026 x86_64 User : onlineadmin ( 10487) PHP Version : 8.5.7 Disable Function : opcache_get_status MySQL : OFF | cURL : ON | WGET : OFF | Perl : OFF | Python : OFF | Sudo : OFF | Pkexec : OFF Directory : /home/httpd/vhosts/comeonline.ch/httpdocs/wp-content/plugins/wpsso/lib/ |
Upload File : |
<?php
/*
* License: GPLv3
* License URI: https://www.gnu.org/licenses/gpl.txt
* Copyright 2012-2026 Jean-Sebastien Morisset (https://wpsso.com/)
*/
if ( ! defined( 'ABSPATH' ) ) {
die( 'These aren\'t the droids you\'re looking for.' );
}
if ( ! defined( 'WPSSO_PLUGINDIR' ) ) {
die( 'Do. Or do not. There is no try.' );
}
if ( ! class_exists( 'WpssoPage' ) ) {
/*
* This class provides methods for the WebPage document.
*
* The use of "Page" in the WpssoPage classname refers to the WebPage document, not WordPress Pages.
*
* For methods related to WordPress Posts, Pages, and custom post types (which are all post objects), see the WpssoPost class.
*/
class WpssoPage { // Aka WpssoWebPage.
private $p; // Wpsso class object.
private $charset;
public function __construct( &$plugin ) {
$this->p =& $plugin;
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$this->charset = get_bloginfo( $show = 'charset', $filter = 'raw' );
/*
* Add information about the page $mod to the body tag class.
*/
add_filter( 'body_class', array( $this, 'add_body_class' ), 1000, 1 );
/*
* Maybe add the Validators toolbar menu.
*/
$add_toolbar_validate = empty( $this->p->options[ 'plugin_add_toolbar_validate' ] ) ? false : true;
$add_toolbar_validate = apply_filters( 'wpsso_add_toolbar_validate', $add_toolbar_validate );
if ( $add_toolbar_validate ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'adding validators toolbar' );
}
add_action( 'admin_bar_menu', array( $this, 'add_admin_bar_menu_validate' ), WPSSO_TB_VALIDATE_MENU_ORDER, 1 );
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'validators toolbar is disabled' );
}
/*
* Maybe add title tag filters.
*
* Since WordPress v4.4.
*
* See wordpress/wp-includes/general-template.php.
*/
if ( ! $this->p->util->is_title_tag_disabled() ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'adding title tag filters' );
}
add_filter( 'pre_get_document_title', array( $this, 'pre_get_document_title' ), WPSSO_TITLE_TAG_PRIORITY, 1 );
add_filter( 'document_title_separator', array( $this, 'document_title_separator' ), WPSSO_TITLE_TAG_PRIORITY, 1 );
add_filter( 'document_title_parts', array( $this, 'document_title_parts' ), WPSSO_TITLE_TAG_PRIORITY, 1 );
add_filter( 'document_title', array( $this, 'document_title' ), WPSSO_TITLE_TAG_PRIORITY, 1 );
add_filter( 'get_wp_title_rss', array( $this, 'get_wp_title_rss' ), WPSSO_TITLE_TAG_PRIORITY, 1 );
add_filter( 'get_bloginfo_rss', array( $this, 'get_bloginfo_rss' ), WPSSO_TITLE_TAG_PRIORITY, 2 );
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'title tag is disabled' );
}
}
/*
* Add information about the page $mod to the body tag class.
*
* $classes = An array of body class names.
*/
public function add_body_class( array $classes ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'required call to WpssoPage->get_mod()' );
}
$mod = $this->get_mod();
$classes[] = 'wpsso-' . SucomUtil::get_mod_css_id( $mod );
return $classes;
}
/*
* This method is hooked to the 'admin_bar_menu' action and receives a reference to the $wp_admin_bar variable.
*
* WpssoPost->ajax_get_validate_submenu() also calls this method directly, supplying the post ID in $post_id.
*/
public function add_admin_bar_menu_validate( &$wp_admin_bar, $post_id = false ) { // Pass by reference OK.
if ( ! $user_id = get_current_user_id() ) { // Just in case.
return;
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'required call to WpssoPage->get_mod()' );
}
$mod = $this->get_mod( $post_id );
if ( $mod[ 'is_comment' ] ) {
$capability = 'edit_comment';
} elseif ( $mod[ 'is_post' ] ) {
$capability = 'page' === $mod[ 'post_type' ] ? 'edit_page' : 'edit_post';
} elseif ( $mod[ 'is_term' ] ) {
$tax_obj = get_taxonomy( $mod[ 'tax_slug' ] );
$capability = $tax_obj->cap->edit_terms;
} elseif ( $mod[ 'is_user' ] ) {
$capability = 'edit_user';
} else return false; // Validators are only provided for known modules (post, term, and user).
if ( ! current_user_can( $capability, $mod[ 'id' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: cannot ' . $capability . ' for ' . $mod[ 'name' ] . ' id ' . $mod[ 'id' ] );
}
return false; // Stop here.
}
/*
* We do not want to validate settings pages in the back-end, so validators are only provided for known
* modules (post, term, and user). If we're on the front-end, validating the current webpage URL is fine.
*/
$validators = $this->p->util->get_validators( $mod, $form = null );
if ( ! empty( $validators ) ) {
$parent_id = 'wpsso-validate';
$menu_icon = '<span class="ab-icon dashicons-code-standards"></span>';
$menu_title = _x( 'Validators', 'toolbar menu title', 'wpsso' );
$menu_items = array();
foreach ( $validators as $key => $el ) {
if ( empty( $el[ 'type' ] ) ) {
continue;
}
$menu_items[ $el[ 'type' ] ] = array(
'id' => $parent_id . '-' . $key,
'title' => $el[ 'type' ],
'parent' => $parent_id,
'href' => $el[ 'url' ],
'group' => false,
'meta' => array(
'class' => empty( $el[ 'url' ] ) ? 'disabled' : '',
'target' => '_blank',
'title' => $el[ 'title' ],
),
);
}
SucomUtil::natksort( $menu_items );
$wp_admin_bar->add_node( array(
'id' => $parent_id,
'title' => $menu_icon . $menu_title,
'parent' => false,
'href' => false,
'group' => false,
'meta' => array(
'html' => '<style type="text/css">#wp-admin-bar-wpsso-validate .disabled { opacity:0.5; filter:alpha(opacity=50); }</style>',
),
) );
foreach ( $menu_items as $menu_item ) {
$wp_admin_bar->add_node( $menu_item );
}
return $parent_id;
}
return false;
}
/*
* Since WordPress v4.4.
*
* Filters the WordPress document title before it is generated. Returning a non-empty value skips the
* 'document_title_separator', 'document_title_parts', and 'document_title' filters.
*
* See wordpress/wp-includes/general-template.php.
*/
public function pre_get_document_title( $pre_title = '' ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'received pre_title value (' . gettype( $pre_title ) . ') = ' . $pre_title );
}
/*
* If $pre_title is not an empty string, then maybe force an empty string to make sure the
* 'document_title_separator', 'document_title_parts', and 'document_title' filters are applied.
*/
if ( '' !== $pre_title ) {
if ( 'wp_title' !== $this->p->options[ 'plugin_title_tag' ] ) {
$pre_title = '';
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'returning an empty string to use document_title filters' );
}
}
}
return $pre_title;
}
/*
* Since WordPress v4.4.
*
* Filters the separator for the document title.
*
* See wordpress/wp-includes/general-template.php.
*/
public function document_title_separator( $title_sep = '-' ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$title_sep = $this->maybe_get_title_sep(); // Returns default title separator (decoded).
return $title_sep;
}
/*
* Since WordPress v4.4.
*
* Filters the parts of the document title.
*
* Array (
* [title] => A Title
* [page] => Page 2
* [site] => Site Name
* [tagline] =>
* )
*
* The final array is imploded into a string using the $title_sep value.
*
* $title = implode( ' ' . $title_sep . ' ', array_filter( $title ) );
*
* See wordpress/wp-includes/general-template.php.
*/
public function document_title_parts( $title_parts ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_arr( 'title_parts (argument)', $title_parts );
$this->p->debug->log( 'required call to WpssoPage->get_mod()' );
}
/*
* Note that in_the_loop() can be true in both archive and singular pages.
*/
$use_post = apply_filters( 'wpsso_use_post', in_the_loop() ? true : false );
$mod = $this->get_mod( $use_post );
if ( $this->p->debug->enabled ) {
$this->p->debug->log_arr( 'mod', $mod );
$this->p->debug->log( 'getting title for ' . $this->p->options[ 'plugin_title_tag' ] );
}
if ( 'wp_title' === $this->p->options[ 'plugin_title_tag' ] ) {
if ( ! empty( $title_parts[ 'site' ] ) && empty( $title_parts[ 'title' ] ) ) {
$title_parts[ 'tagline' ] = true; // Add the tagline before returning the array.
}
} else {
$md_key = $this->p->options[ 'plugin_title_tag' ];
$max_len = $this->p->options[ 'plugin_title_tag' ];
/*
* Return a decoded title, which may be used in the RSS XML <title> tag.
*/
$title_parts[ 'title' ] = $this->get_title( $mod, $md_key, $max_len, $title_sep = null, $num_hashtags = false, $do_encode = false );
if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive).
unset( $title_parts[ 'site' ] ); // The title from WPSSO will be the site name.
$title_parts[ 'tagline' ] = true; // Add the tagline before returning the array.
}
unset( $title_parts[ 'page' ] ); // The title from WPSSO includes the page number.
}
if ( ! empty( $title_parts[ 'site' ] ) ) {
$title_parts[ 'site' ] = $this->p->opt->get_text( 'plugin_title_part_site' );
}
if ( ! empty( $title_parts[ 'tagline' ] ) ) {
$title_parts[ 'tagline' ] = $this->p->opt->get_text( 'plugin_title_part_tagline' );
}
/*
* Make sure the parts are ordered properly (just in case).
*/
$title_parts = array_merge( array( 'title' => null, 'page' => null, 'site' => null, 'tagline' => null ), $title_parts );
if ( $this->p->debug->enabled ) {
$this->p->debug->log_arr( 'title_parts (returned)', $title_parts );
}
return $title_parts;
}
/*
* Since WordPress v4.4.
*
* Filters the document title.
*
* See wordpress/wp-includes/general-template.php.
*/
public function document_title( $title ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'title = ' . $title );
}
if ( false !== strpos( $title, '%%' ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'required call to WpssoPage->get_mod()' );
}
/*
* Note that in_the_loop() can be true in both archive and singular pages.
*/
$use_post = apply_filters( 'wpsso_use_post', in_the_loop() ? true : false );
$mod = $this->get_mod( $use_post );
$title = $this->p->util->inline->replace_variables( $title, $mod );
}
return SucomUtil::encode_html_emoji( $title ); // Does not double-encode.
}
/*
* Since WPSSO Core v15.20.0.
*
* Filters the string from wp_get_document_title() for get_wp_title_rss() and wp_title_rss() to convert HTML named
* entities into numbered entities.
*
* See wordpress/wp-includes/feed.php:
* return apply_filters( 'get_wp_title_rss', wp_get_document_title(), $deprecated );
*
* See wordpress/wp-includes/feed-rdf.php:
* <title><?php wp_title_rss(); ?></title>
*
* See wordpress/wp-includes/feed-rss2.php:
* <title><?php wp_title_rss(); ?></title>
*
* See wordpress/wp-includes/feed-rss2-comments.php:
* <title><?php printf( ent2ncr( __( 'Comments for %s' ) ), get_wp_title_rss() ); ?></title>
*
* See wordpress/wp-includes/feed-rss.php:
* <title><?php wp_title_rss(); ?></title>
*/
public function get_wp_title_rss( $wp_title ) {
return ent2ncr( $wp_title ); // Converts named entities into numbered entities.
}
/*
* Since WPSSO Core v15.20.0.
*
* Filters the string from get_bloginfo() for get_bloginfo_rss() and bloginfo_rss() to encode special characters
* and convert HTML named entities into numbered entities.
*
* See https://developer.wordpress.org/reference/functions/get_bloginfo/.
*/
public function get_bloginfo_rss( $bloginfo, $show ) {
$bloginfo = SucomUtil::encode_html_emoji( $bloginfo ); // Does not double-encode.
return ent2ncr( $bloginfo ); // Converts named entities into numbered entities.
}
/*
* Public method to sanitize arguments or modify values for get_title(), get_description(), etc.
*
* The $mod array argument is preferred but not required.
*
* $mod = true | false | post_id | $mod array
*/
public function maybe_get_mod( $mod ) {
if ( ! is_array( $mod ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'optional call to WpssoPage->get_mod()' );
}
$mod = $this->get_mod( $mod );
}
return $mod;
}
/*
* Determine and return the post/user/term module array.
*
* If any custom modifications are required to the WP_Query 'query_vars', they should be done before the 'wp_head'
* action is triggered. The WpssoHead->show_head() method calls WpssoPage->get_mod() to determine the current
* WordPress object (comment, post, term, or user), if any, and saves the 'query_vars' value for WordPress archive
* queries.
*/
public function get_mod( $use_post = false, $mod = false, $wp_obj = false ) {
if ( ! is_array( $mod ) ) {
$mod = array();
} elseif ( isset( $mod[ 'obj' ] ) && is_object( $mod[ 'obj' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: module object is already defined' );
}
return $mod;
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'use_post is ' . SucomUtil::get_use_post_string( $use_post ) );
}
/*
* Check for known WP objects and set the object module name and its object ID.
*/
if ( is_object( $wp_obj ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'wp_obj argument is ' . get_class( $wp_obj ) . ' object' );
}
switch ( get_class( $wp_obj ) ) {
case 'WP_Comment':
$mod[ 'name' ] = 'comment';
$mod[ 'id' ] = $wp_obj->comment_ID;
break;
case 'WP_Post':
$mod[ 'name' ] = 'post';
$mod[ 'id' ] = $wp_obj->ID;
break;
case 'WP_Term':
$mod[ 'name' ] = 'term';
$mod[ 'id' ] = $wp_obj->term_id;
break;
case 'WP_User':
$mod[ 'name' ] = 'user';
$mod[ 'id' ] = $wp_obj->ID;
break;
default:
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'unknown wp object class = ' . get_class( $wp_obj ) );
}
break;
}
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'wp_obj argument is not an object' );
}
unset( $wp_obj ); // Done with $wp_obj.
if ( empty( $mod[ 'name' ] ) ) {
if ( SucomUtilWP::is_post_page( $use_post ) ) { // $use_post = true | false | post ID.
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'is_post_page is true' );
}
$mod[ 'name' ] = 'post';
} elseif ( SucomUtilWP::is_term_page() ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'is_term_page is true' );
}
$mod[ 'name' ] = 'term';
} elseif ( SucomUtilWP::is_user_page() ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'is_user_page is true' );
}
$mod[ 'name' ] = 'user';
} else $mod[ 'name' ] = false;
}
if ( empty( $mod[ 'id' ] ) ) {
switch ( $mod[ 'name' ] ) {
case 'comment':
$mod[ 'id' ] = SucomUtilWP::get_comment_object( false, 'id' );
break;
case 'post':
$mod[ 'id' ] = SucomUtilWP::get_post_object( $use_post, 'id' ); // $use_post = true | false | post_id
break;
case 'term':
$mod[ 'id' ] = SucomUtilWP::get_term_object( false, '', 'id' );
break;
case 'user':
$mod[ 'id' ] = SucomUtilWP::get_user_object( false, 'id' );
break;
default:
$mod[ 'id' ] = false;
break;
}
}
if ( ! empty( $mod[ 'name' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'getting $mod array from ' . $mod[ 'name' ] . ' module object' );
}
}
switch ( $mod[ 'name' ] ) {
case 'comment':
$mod = $this->p->comment->get_mod( $mod[ 'id' ] );
break;
case 'post':
$mod = $this->p->post->get_mod( $mod[ 'id' ] );
break;
case 'term':
$mod = $this->p->term->get_mod( $mod[ 'id' ] );
break;
case 'user':
$mod = $this->p->user->get_mod( $mod[ 'id' ] );
break;
default:
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'unknown module object - merging $mod defaults array' );
}
$mod = array_merge( WpssoAbstractWpMeta::get_mod_defaults(), $mod );
break;
}
/*
* WpssoPage elements.
*/
global $wp_query;
$mod[ 'query_vars' ] = $wp_query->query_vars;
if ( empty( $mod[ 'paged' ] ) ) { // False by default.
if ( ! empty( $mod[ 'query_vars' ][ 'page' ] ) ) {
$mod[ 'paged' ] = $mod[ 'query_vars' ][ 'page' ];
} elseif ( ! empty( $mod[ 'query_vars' ][ 'paged' ] ) ) {
$mod[ 'paged' ] = $mod[ 'query_vars' ][ 'paged' ];
}
}
/*
* Note that 'paged_total' can be pre-defined by WpssoPost->get_mod() for posts with content (ie. singular)
* and paging in their content.
*/
if ( empty( $mod[ 'paged_total' ] ) ) { // False by default.
if ( ! empty( $wp_query->max_num_pages ) ) {
$mod[ 'paged_total' ] = $wp_query->max_num_pages;
}
}
if ( $mod[ 'paged' ] && $mod[ 'paged_total' ] && $mod[ 'paged' ] > $mod[ 'paged_total' ] ) { // Just in case.
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'paged greater than paged_total - adjusting paged value' );
}
$mod[ 'paged' ] = $mod[ 'paged_total' ];
}
if ( empty( $mod[ 'comment_paged' ] ) ) { // False by default.
if ( ! empty( $mod[ 'query_vars' ][ 'cpage' ] ) ) {
$mod[ 'comment_paged' ] = $mod[ 'query_vars' ][ 'cpage' ];
}
}
$mod[ 'use_post' ] = $use_post;
if ( empty( $mod[ 'name' ] ) ) { // Not a comment, post, term, or user object.
if ( is_home() ) {
$mod[ 'is_home' ] = true; // Home page (static or blog archive).
$mod[ 'is_home_posts' ] = true; // Static posts page or blog archive page.
} elseif ( is_feed() ) {
$mod[ 'is_feed' ] = true;
} elseif ( is_404() ) {
$mod[ 'is_404' ] = true;
} elseif ( is_search() ) {
$mod[ 'is_search' ] = true;
} elseif ( is_archive() ) {
$mod[ 'is_archive' ] = true;
if ( is_date() ) {
$mod[ 'is_date' ] = true;
if ( is_year() ) {
$mod[ 'is_year' ] = true;
} elseif ( is_month() ) {
$mod[ 'is_month' ] = true;
} elseif ( is_day() ) {
$mod[ 'is_day' ] = true;
}
}
}
} elseif ( empty( $mod[ 'id' ] ) ) {
switch ( $mod[ 'name' ] ) {
case 'post':
/*
* If the query is a post with an ID of 0, then it may be a post type archive page
* that was setup incorrectly (ie. a post object without an ID or slug).
*/
if ( ! empty( $mod[ 'query_vars' ][ 'post_type' ] ) ) {
$mod[ 'post_type' ] = $mod[ 'query_vars' ][ 'post_type' ];
$post_type_obj = get_post_type_object( $mod[ 'post_type' ] );
if ( is_object( $post_type_obj ) ) { // Just in case.
if ( isset( $post_type_obj->labels->name ) ) {
$mod[ 'post_type_label_plural' ] = $post_type_obj->labels->name;
}
if ( isset( $post_type_obj->labels->singular_name ) ) {
$mod[ 'post_type_label_single' ] = $post_type_obj->labels->singular_name;
}
if ( isset( $post_type_obj->public ) ) {
$mod[ 'is_public' ] = $post_type_obj->public ? true : false;
}
if ( is_post_type_archive() ) {
$mod[ 'is_post_type_archive' ] = true;
$mod[ 'is_archive' ] = $mod[ 'is_post_type_archive' ];
}
}
unset( $post_type_obj ); // Done with $post_type_obj.
}
break;
}
}
return $mod;
}
/*
* See WpssoFaqShortcodeFaq->do_shortcode().
* See WpssoSchema->add_itemlist_data().
* See WpssoSchema->add_posts_data().
*/
public function get_posts_mods( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$page_posts_mods = array();
if ( ! empty( $mod[ 'query_vars' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'using WP_Query query_vars to get post mods' );
}
global $wp_query;
$saved_wp_query = $wp_query;
$wp_query = new WP_Query( $mod[ 'query_vars' ] );
if ( $mod[ 'is_home_posts' ] ) { // Static posts page or blog archive page.
$wp_query->is_home = true;
}
if ( have_posts() ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'looping through posts' );
}
$have_num = 0;
while ( have_posts() ) {
$have_num++;
the_post(); // Defines the $post global.
global $post;
if ( $this->p->debug->enabled ) {
$this->p->debug->log( '#' . $have_num . ': getting mod for post ID ' . $post->ID );
}
$page_posts_mods[] = $this->p->post->get_mod( $post->ID );
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'retrieved ' . $have_num . ' post mods' );
}
rewind_posts();
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'no posts to add' );
}
$wp_query = $saved_wp_query;
} elseif ( ! empty( $mod[ 'obj' ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'using module object to get post mods' );
}
$page_posts_mods = $mod[ 'obj' ]->get_posts_mods( $mod );
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'no source to get post mods' );
}
$page_posts_mods = apply_filters( 'wpsso_page_posts_mods', $page_posts_mods, $mod );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'returning ' . count( $page_posts_mods ) . ' post mods' );
}
return $page_posts_mods;
}
/*
* $mod = true | false | post_id | array
*
* $md_key = true | false | string | array
*
* $caption_type = 'title' | 'excerpt' | 'both'
*
* See WpssoRrssbFiltersEdit->filter_post_edit_share_rows().
*/
public function get_caption( $mod = false, $md_key = null, $caption_type = 'title', $caption_max_len = 0, $num_hashtags = false, $do_encode = true ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'mod' => $mod,
'md_key' => $md_key,
'caption_type' => $caption_type,
'caption_max_len' => $caption_max_len,
'num_hashtags' => $num_hashtags, // True, false, or numeric.
'do_encode' => $do_encode,
) );
}
switch ( $caption_type ) {
case 'title':
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_title' ); // Returns an array of metadata keys (can be empty).
break;
case 'excerpt':
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_desc' ); // Returns an array of metadata keys (can be empty).
break;
case 'both':
default:
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'og_caption' ); // Returns an array of metadata keys (can be empty).
break;
}
$caption_max_len = $this->sanitize_max_len( $caption_max_len ); // Returns max integer for numeric, string, or array value.
$mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided.
$caption_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value.
$is_custom = empty( $caption_text ) ? false : true;
/*
* If there's no custom caption text, then go ahead and generate the caption text value.
*/
if ( empty( $caption_text ) ) { // No custom value found.
/*
* Request all values un-encoded, then encode once we have the complete caption text.
*/
switch ( $caption_type ) {
case 'title':
$caption_text = $this->get_title( $mod, $md_key_title = 'og_title', $caption_max_len,
$title_sep = null, $num_hashtags, $do_encode_title = false );
break;
case 'excerpt':
$caption_text = $this->get_description( $mod, $md_key_desc = 'og_desc', $caption_max_len,
$num_hashtags, $do_encode_desc = false );
break;
case 'both':
$title_sep = $this->maybe_get_title_sep(); // Returns default title separator (decoded).
$caption_text = $this->get_title( $mod, $md_key_title = 'og_title', $max_len = 'og_title',
$title_sep, $num_hashtags_title = false, $do_encode_title = false );
$caption_text_len = strlen( trim( $caption_text . ' ' . $title_sep ) . ' ' );
$adj_max_len = $caption_max_len - $caption_text_len;
$caption_desc = $this->get_description( $mod, $md_key_desc = 'og_desc', $adj_max_len,
$num_hashtags, $do_encode_desc = false );
$this->add_title_part( $caption_text, $title_sep, $caption_desc );
break;
}
}
if ( true === $do_encode ) {
$caption_text = SucomUtil::encode_html_emoji( $caption_text ); // Does not double-encode.
} else $caption_text = html_entity_decode( SucomUtil::decode_utf8( $caption_text ), ENT_QUOTES, $this->charset );
return apply_filters( 'wpsso_caption', $caption_text, $mod, $num_hashtags, $md_key );
}
/*
* $mod = true | false | post_id | array
*
* $md_key = true | false | string | array
*
* Use $title_sep = false to avoid adding term parent names in the term title.
*
* WpssoUtilInline->replace_variables() is applied to the final title text.
*
* The WPSSO BC add-on uses $title_sep = false to avoid prefixing term parents in the term titles.
*/
public function get_title( $mod = false, $md_key = null, $max_len = 0, $title_sep = null, $num_hashtags = false, $do_encode = true ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'mod' => $mod,
'md_key' => $md_key,
'max_len' => $max_len,
'title_sep' => $title_sep,
'num_hashtags' => $num_hashtags, // True, false, or numeric.
'do_encode' => $do_encode,
) );
}
$mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided.
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'seo_title' ); // Returns an array of metadata keys (can be empty).
$max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value.
$dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded).
$title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided.
$title_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value.
$is_custom = empty( $title_text ) ? false : true;
$hashtags = '';
/*
* Get seed if no custom meta title.
*/
if ( empty( $title_text ) ) {
$title_text = apply_filters( 'wpsso_title_seed', '', $mod, $num_hashtags, $md_key, $title_sep );
if ( ! empty( $title_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'title seed = ' . $title_text );
}
}
}
/*
* If there's no custom title, and no pre-seed, then go ahead and generate the title value.
*
* A custom or pre-seed title is expected to provide any hashtags, so get hashtags only when generating the
* title value.
*/
if ( empty( $title_text ) ) {
$title_text = $this->get_the_title( $mod, $title_sep );
$hashtags = $this->get_hashtags( $mod, $num_hashtags );
}
/*
* Add the page number if it's greater than 1 and we don't already have a '%%page%%' or '%%pagenumber%%'
* inline variable in the title.
*/
$page_number_transl = '';
if ( $mod[ 'paged' ] > 1 ) {
if ( false === strpos( $title_text, '%%page%%' ) && false === strpos( $title_text, '%%pagenumber%%' ) ) {
if ( $mod[ 'paged_total' ] > 1 ) {
$page_number_transl = sprintf( __( 'Page %1$d of %2$d', 'wpsso' ), $mod[ 'paged' ], $mod[ 'paged_total' ] );
} else {
$page_number_transl = sprintf( __( 'Page %1$d', 'wpsso' ), $mod[ 'paged' ] );
}
}
}
/*
* Titles comprised entirely of HTML content will be empty after running cleanup_html_tags(), so remove the
* HTML tags before maybe falling back to the generic title.
*/
$title_text = $this->p->util->cleanup_html_tags( $title_text, $strip_tags = true, $use_img_alt = true );
/*
* If there's still no title, then fallback to a generic version.
*/
if ( empty( $title_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'title is empty - using generic title text' );
}
$title_text = $this->p->opt->get_text( 'plugin_no_title_text' ); // No Title Text.
}
/*
* Replace any inline variables in the string before checking/adjusting its length.
*/
if ( false !== strpos( $title_text, '%%' ) ) {
/*
* Override the default 'title_sep' value.
*/
$title_text = $this->p->util->inline->replace_variables( $title_text, $mod, $atts = array( 'title_sep' => $title_sep ) );
}
/*
* Check title against string length limits.
*/
if ( $max_len > 0 ) {
/*
* If we have a page number, reduce the max length by the separator, page number, and two spaces.
*/
$adj_max_len = empty( $page_number_transl ) ? $max_len : $max_len - strlen ( $title_sep ) - strlen( $page_number_transl ) - 2;
/*
* If we have any hashtags, further reduce the max title length by the hashtags and one space.
*/
$adj_max_len = empty( $hashtags ) ? $adj_max_len : $adj_max_len - strlen( $hashtags ) - 1;
$title_text = $this->p->util->limit_text_length( $title_text, $adj_max_len, $dots, $cleanup_html = false );
}
/*
* Once the title length has been adjusted, we can add the page number and hashtags.
*/
if ( ! empty( $page_number_transl ) ) {
$this->add_title_part( $title_text, $title_sep, $page_number_transl );
}
if ( ! empty( $hashtags ) ) {
$this->add_title_part( $title_text, '', $hashtags );
}
/*
* Maybe return the values encoded (true by default).
*/
if ( $do_encode ) {
$title_text = SucomUtil::encode_html_emoji( $title_text ); // Does not double-encode.
$title_sep = SucomUtil::encode_html_emoji( $title_sep ); // Does not double-encode.
}
return apply_filters( 'wpsso_title', $title_text, $mod, $num_hashtags, $md_key, $title_sep, $is_custom );
}
/*
* $mod = true | false | post_id | array.
*
* $md_key = true | false | string | array.
*
* WpssoUtilInline->replace_variables() is applied to the final description text.
*/
public function get_description( $mod = false, $md_key = null, $max_len = 0, $num_hashtags = false, $do_encode = true ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'mod' => $mod,
'md_key' => $md_key,
'max_len' => $max_len,
'num_hashtags' => $num_hashtags, // True, false, or numeric.
'do_encode' => $do_encode,
) );
}
$mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided.
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'seo_desc' ); // Returns an array of metadata keys (can be empty).
$max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value.
$dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded).
$desc_text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value.
$is_custom = empty( $desc_text ) ? false : true;
$hashtags = '';
/*
* Get seed if no custom meta description.
*/
if ( empty( $desc_text ) ) {
$desc_text = apply_filters( 'wpsso_description_seed', '', $mod, $num_hashtags, $md_key );
if ( ! empty( $desc_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'description seed = ' . $desc_text );
}
}
}
/*
* If there's no custom description, and no pre-seed, then go ahead and generate the description value.
*
* A custom or pre-seed description is expected to provide any hashtags, so get hashtags only when
* generating the description value.
*/
if ( empty( $desc_text ) ) {
$desc_text = $this->get_the_description( $mod );
$hashtags = $this->get_hashtags( $mod, $num_hashtags );
}
/*
* Descriptions comprised entirely of HTML content will be empty after running cleanup_html_tags(), so
* remove the HTML tags before maybe falling back to the generic description.
*/
$desc_text = $this->p->util->cleanup_html_tags( $desc_text, $strip_tags = true, $use_img_alt = true );
/*
* If there's still no description, then fallback to a generic version.
*/
if ( empty( $desc_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'description is empty - using generic description text' );
}
$desc_text = $this->p->opt->get_text( 'plugin_no_desc_text' ); // No Description Text.
}
/*
* Replace any inline variables in the string before checking/adjusting its length.
*/
if ( false !== strpos( $desc_text, '%%' ) ) {
$desc_text = $this->p->util->inline->replace_variables( $desc_text, $mod );
}
/*
* Check description against string length limits.
*/
if ( $max_len > 0 ) {
/*
* If we have any hashtags, reduce the max length by the hashtags and one space.
*/
$adj_max_len = empty( $hashtags ) ? $max_len : $max_len - strlen( $hashtags ) - 1;
$desc_text = $this->p->util->limit_text_length( $desc_text, $adj_max_len, $dots, $cleanup_html = false );
}
/*
* Once the description length has been adjusted, we can add the hashtags.
*/
if ( ! empty( $hashtags ) ) {
$this->add_title_part( $desc_text, '', $hashtags );
}
/*
* Maybe return the values encoded (true by default).
*/
if ( $do_encode ) {
$desc_text = SucomUtil::encode_html_emoji( $desc_text ); // Does not double-encode.
}
return apply_filters( 'wpsso_description', $desc_text, $mod, $num_hashtags, $md_key, $is_custom );
}
public function get_text( $mod = false, $md_key = null, $max_len = 0, $num_hashtags = false, $do_encode = true ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log_args( array(
'mod' => $mod,
'md_key' => $md_key,
'max_len' => $max_len,
'num_hashtags' => $num_hashtags, // True, false, or numeric.
'do_encode' => $do_encode,
) );
}
$mod = $this->maybe_get_mod( $mod ); // Returns $mod array if not provided.
$md_key = $this->sanitize_md_key( $md_key, $def_key = 'schema_text' ); // Returns an array of metadata keys (can be empty).
$max_len = $this->sanitize_max_len( $max_len ); // Returns max integer for numeric, string, or array value.
$dots = $this->maybe_get_ellipsis(); // Returns default ellipsis (decoded).
$text = $this->maybe_get_opt_multi( $mod, $md_key ); // Returns null or custom value.
$is_custom = empty( $text ) ? false : true;
/*
* If there's no custom text, then go ahead and generate the text value.
*/
if ( empty( $title_text ) ) {
$text = $this->get_the_text( $mod );
$hashtags = $this->get_hashtags( $mod, $num_hashtags );
}
/*
* Check text against string length limits.
*/
if ( $max_len > 0 ) {
/*
* If we have any hashtags, reduce the max length by the hashtags and one space.
*/
$adj_max_len = empty( $hashtags ) ? $max_len : $max_len - strlen( $hashtags ) - 1;
$text = $this->p->util->limit_text_length( $text, $adj_max_len, $dots, $cleanup_html = false );
}
/*
* Once the text length has been adjusted, we can add the hashtags.
*/
if ( ! empty( $hashtags ) ) {
$this->add_title_part( $text, '', $hashtags );
}
/*
* Maybe return the values encoded (true by default).
*/
if ( $do_encode ) {
$text = SucomUtil::encode_html_emoji( $text ); // Does not double-encode.
}
return apply_filters( 'wpsso_text', $text, $mod, $num_hashtags, $md_key, $is_custom );
}
/*
* Use $title_sep = false to avoid adding term parent names in the term title.
*
* WpssoUtilInline->replace_variables() is called from the WpssoPage->get_title() method. Do not use
* WpssoUtilInline->replace_variables() in this method as WpssoPage->get_title() is also called from
* WpssoUtilInline->replace_variables(), and this would create a circular loop.
*
* See WpssoBcBreadcrumb->add_breadcrumblist_data().
* See WpssoPage->get_title().
* See WpssoUtilInline->replace_callback().
*/
public function get_the_title( array $mod, $title_sep = null ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided.
$title_text = '';
/*
* Similar module type logic can be found in the following methods:
*
* See WpssoOpenGraph->get_mod_og_type().
* See WpssoPage->get_the_title().
* See WpssoPage->get_the_description().
* See WpssoSchema->get_mod_schema_type().
* See WpssoUtil->get_canonical_url().
*/
if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive).
$title_text = SucomUtilWP::get_site_name( $this->p->options );
} elseif ( $mod[ 'is_comment' ] ) {
if ( is_numeric( $mod[ 'comment_rating' ] ) ) {
$title_text = $this->p->opt->get_text( 'plugin_comment_review_title' );
} elseif ( $mod[ 'comment_parent' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_comment_reply_title' );
} else $title_text = $this->p->opt->get_text( 'plugin_comment_title' );
} elseif ( $mod[ 'is_post' ] ) {
if ( $mod[ 'post_type' ] && is_string( $mod[ 'post_type' ] ) ) { // Just in case.
if ( $mod[ 'is_post_type_archive' ] ) { // The post ID may be 0.
$title_text = $this->p->opt->get_text( 'plugin_pta_' . $mod[ 'post_type' ] . '_title' );
if ( empty( $title_text ) ) { // Just in case.
$post_type_obj = get_post_type_object( $mod[ 'post_type' ] );
if ( isset( $post_type_obj->label ) ) { // Just in case.
$title_text = $post_type_obj->label;
}
}
} elseif ( $mod[ 'wp_obj' ] instanceof WP_Post ) {
/*
* Note that get_the_title() applies the 'the_title' filter, not the 'wp_title' filter.
*/
$title_text = get_the_title( $mod[ 'wp_obj' ] );
} elseif ( $mod[ 'id' ] ) {
/*
* Note that get_the_title() applies the 'the_title' filter, not the 'wp_title' filter.
*/
$title_text = get_the_title( $mod[ 'id' ] );
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'no post object or id' );
}
$title_text = html_entity_decode( $title_text, ENT_QUOTES, $this->charset );
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'no post type' );
}
} elseif ( $mod[ 'is_term' ] ) {
/*
* The default 'plugin_term_page_title' value is '%%term_hierarchy%%'.
*
* If $title_sep is false, then use '%%term_title%%' instead.
*/
if ( false === $title_sep ) {
$title_text = '%%term_title%%';
} else $title_text = $this->p->opt->get_text( 'plugin_term_page_title' );
} elseif ( $mod[ 'is_user' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_author_page_title' );
} elseif ( $mod[ 'is_feed' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_feed_title' );
} elseif ( $mod[ 'is_404' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_404_page_title' );
} elseif ( $mod[ 'is_search' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_search_page_title' );
} elseif ( $mod[ 'is_archive' ] ) {
if ( $mod[ 'is_date' ] ) {
if ( $mod[ 'is_year' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_year_page_title' );
} elseif ( $mod[ 'is_month' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_month_page_title' );
} elseif ( $mod[ 'is_day' ] ) {
$title_text = $this->p->opt->get_text( 'plugin_day_page_title' );
}
}
}
return apply_filters( 'wpsso_the_title', $title_text, $mod, $title_sep );
}
/*
* WpssoUtilInline->replace_variables() is called from the WpssoPage->get_description() method. Do not use
* WpssoUtilInline->replace_variables() in this method as WpssoPage->get_description() is also called from
* WpssoUtilInline->replace_variables(), and this would create a circular loop.
*
* See WpssoPage->get_description().
* See WpssoUtilInline->replace_callback().
*/
public function get_the_description( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$desc_text = '';
/*
* Similar module type logic can be found in the following methods:
*
* See WpssoOpenGraph->get_mod_og_type().
* See WpssoPage->get_the_title().
* See WpssoPage->get_the_description().
* See WpssoSchema->get_mod_schema_type().
* See WpssoUtil->get_canonical_url().
*/
if ( $mod[ 'is_home' ] ) { // Home page (static or blog archive).
if ( $mod[ 'is_post' ] && $mod[ 'id' ] ) {
$desc_text = $this->get_the_excerpt( $mod );
}
if ( empty( $desc_text ) ) {
$desc_text = SucomUtilWP::get_site_description( $this->p->options );
}
} elseif ( $mod[ 'is_comment' ] ) {
if ( $mod[ 'wp_obj' ] instanceof WP_Comment ) {
$desc_text = get_comment_excerpt( $mod[ 'wp_obj' ] );
} elseif ( $mod[ 'id' ] ) { // Just in case.
$desc_text = get_comment_excerpt( $mod[ 'id' ] );
}
} elseif ( $mod[ 'is_post' ] ) {
if ( $mod[ 'post_type' ] && is_string( $mod[ 'post_type' ] ) ) { // Just in case.
if ( $mod[ 'is_post_type_archive' ] ) { // The post ID may be 0.
$desc_text = $this->p->opt->get_text( 'plugin_pta_' . $mod[ 'post_type' ] . '_desc' );
if ( empty( $desc_text ) ) { // Just in case.
$post_type_obj = get_post_type_object( $mod[ 'post_type' ] );
if ( ! empty( $post_type_obj->description ) ) {
$desc_text = $post_type_obj->description;
}
}
} else {
$desc_text = $this->get_the_excerpt( $mod );
/*
* If there's no excerpt, then fallback to the content.
*/
if ( empty( $desc_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'getting the content for post ID ' . $mod[ 'id' ] );
}
$desc_text = $this->get_the_content( $mod );
/*
* Ignore everything before the first paragraph.
*/
if ( ! empty( $desc_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'removing text before the first paragraph' );
}
/*
* U = Inverts the "greediness" of quantifiers so that they are not greedy by default.
* i = Letters in the pattern match both upper and lower case letters.
*
* See http://php.net/manual/en/reference.pcre.pattern.modifiers.php.
*/
$desc_text = preg_replace( '/^.*<p[^>]*>/Usi', '', $desc_text );
}
}
/*
* Fallback to the image alt value.
*/
if ( empty( $desc_text ) ) {
if ( $mod[ 'is_attachment' ] && 'image' === $mod[ 'post_mime_group' ] ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'falling back to attachment image alt text' );
}
$desc_text = get_metadata( 'post', $mod[ 'id' ], '_wp_attachment_image_alt', $single = true );
}
}
}
} elseif ( $this->p->debug->enabled ) {
$this->p->debug->log( 'no post type' );
}
} elseif ( $mod[ 'is_term' ] ) {
if ( $mod[ 'wp_obj' ] instanceof WP_Term ) {
if ( isset( $mod[ 'wp_obj' ]->description ) ) {
$desc_text = $mod[ 'wp_obj' ]->description;
}
} elseif ( $mod[ 'id' ] ) {
$term_obj = SucomUtilWP::get_term_object( $mod[ 'id' ], $mod[ 'tax_slug' ] );
if ( isset( $term_obj->description ) ) {
$desc_text = $term_obj->description;
}
}
if ( '' === $desc_text ) {
$desc_text = $this->p->opt->get_text( 'plugin_term_page_desc' );
}
} elseif ( $mod[ 'is_user' ] ) {
if ( $mod[ 'wp_obj' ] instanceof WP_User ) {
if ( isset( $mod[ 'wp_obj' ]->description ) ) {
$desc_text = $mod[ 'wp_obj' ]->description;
}
} elseif ( $mod[ 'id' ] ) { // Just in case.
$user_obj = SucomUtilWP::get_user_object( $mod[ 'id' ] );
if ( isset( $user_obj->description ) ) {
$desc_text = $user_obj->description;
}
}
if ( '' === $desc_text ) {
$desc_text = $this->p->opt->get_text( 'plugin_author_page_desc' );
}
} elseif ( $mod[ 'is_404' ] ) {
$desc_text = $this->p->opt->get_text( 'plugin_404_page_desc' );
} elseif ( $mod[ 'is_search' ] ) {
$desc_text = $this->p->opt->get_text( 'plugin_search_page_desc' );
} elseif ( $mod[ 'is_archive' ] ) {
if ( $mod[ 'is_date' ] ) {
if ( $mod[ 'is_year' ] ) {
$desc_text = $this->p->opt->get_text( 'plugin_year_page_desc' );
} elseif ( $mod[ 'is_month' ] ) {
$desc_text = $this->p->opt->get_text( 'plugin_month_page_desc' );
} elseif ( $mod[ 'is_day' ] ) {
$desc_text = $this->p->opt->get_text( 'plugin_day_page_desc' );
}
}
}
return apply_filters( 'wpsso_the_description', $desc_text, $mod );
}
public function get_the_excerpt( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$excerpt_text = '';
if ( $mod[ 'is_post' ] ) { // Only post objects have excerpts.
if ( $mod[ 'wp_obj' ] instanceof WP_Post ) {
$excerpt_text = get_post_field( 'post_excerpt', $mod[ 'wp_obj' ] );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'get_post_field() for WP_Post = ' . $excerpt_text );
}
} elseif ( $mod[ 'id' ] ) {
$excerpt_text = get_post_field( 'post_excerpt', $mod[ 'id' ] );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'get_post_field() for ' . $mod[ 'name' ] . ' id ' . $mod[ 'id' ] . ' = ' . $excerpt_text );
}
}
$filter_excerpt = empty( $this->p->options[ 'plugin_filter_excerpt' ] ) ? false : true;
$filter_excerpt = apply_filters( 'wpsso_the_excerpt_filter_excerpt', $filter_excerpt );
if ( $filter_excerpt ) {
$excerpt_text = $this->p->util->safe_apply_filters( array( 'get_the_excerpt', $excerpt_text, $mod[ 'wp_obj' ] ), $mod );
if ( ! is_string( $excerpt_text ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'excerpt_text from "get_the_excerpt" is ' . gettype( $content ) );
}
$excerpt_text = '';
}
}
}
return apply_filters( 'wpsso_the_excerpt', $excerpt_text, $mod );
}
/*
* The cache is cleared by WpssoAbstractWpMeta->clear_mod_cache().
*/
public function clear_the_content( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
/*
* Note that the sort order, page number, locale, amp and embed checks are provided by
* WpssoHead->get_head_cache_index() and not SucomUtil::get_mod_salt().
*/
$canonical_url = $this->p->util->get_canonical_url( $mod );
$cache_md5_pre = 'wpsso_c_';
$cache_salt = __CLASS__ . '::the_content(' . SucomUtil::get_mod_salt( $mod, $canonical_url ) . ')';
$cache_id = $cache_md5_pre . md5( $cache_salt );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'canonical url = ' . $canonical_url );
$this->p->debug->log( 'wp cache salt = ' . $cache_salt );
$this->p->debug->log( 'wp cache id = ' . $cache_id );
}
wp_cache_delete( $cache_id );
return;
}
/*
* The cache is cleared by WpssoAbstractWpMeta->clear_mod_cache().
*/
public function get_the_content( array $mod, $flatten = true ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark(); // Log execution time and memory usage.
$this->p->debug->log_args( array(
'mod' => $mod,
) );
}
/*
* Note that the sort order, page number, locale, amp and embed checks are provided by
* WpssoHead->get_head_cache_index() and not SucomUtil::get_mod_salt().
*/
$filter_content = empty( $this->p->options[ 'plugin_filter_content' ] ) ? false : true;
$filter_content = apply_filters( 'wpsso_the_content_filter_content', $filter_content );
$filter_blocks = apply_filters( 'wpsso_the_content_filter_blocks', true );
$canonical_url = $this->p->util->get_canonical_url( $mod );
$cache_md5_pre = 'wpsso_c_';
$cache_exp_secs = $this->p->util->get_cache_exp_secs( $cache_md5_pre, $cache_type = 'wp_cache' );
$cache_salt = __CLASS__ . '::the_content(' . SucomUtil::get_mod_salt( $mod, $canonical_url ) . ')';
$cache_id = $cache_md5_pre . md5( $cache_salt );
$cache_index = 'locale:' . SucomUtilWP::get_locale( $mod ) . '_filter:' . ( $filter_content ? 'true' : 'false' );
$cache_array = array();
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'canonical url = ' . $canonical_url );
$this->p->debug->log( 'filter content = ' . ( $filter_content ? 'true' : 'false' ) );
$this->p->debug->log( 'wp cache expire = ' . $cache_exp_secs );
$this->p->debug->log( 'wp cache salt = ' . $cache_salt );
$this->p->debug->log( 'wp cache id = ' . $cache_id );
$this->p->debug->log( 'wp cache index = ' . $cache_index );
}
if ( $cache_exp_secs > 0 ) {
$cache_array = wp_cache_get( $cache_id, __METHOD__ );
if ( isset( $cache_array[ $cache_index ] ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'exiting early: cache index found in wp_cache' );
}
$content =& $cache_array[ $cache_index ];
/*
* Maybe put everything on one line but do not cache the re-formatted content.
*/
return $flatten ? preg_replace( '/[\s\r\n]+/s', ' ', $content ) : $content;
} else {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'cache index not in wp_cache' );
}
if ( ! is_array( $cache_array ) ) {
$cache_array = array();
}
}
}
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'initializing new wp cache element' );
}
$cache_array[ $cache_index ] = ''; // Initialize the cache element.
$content =& $cache_array[ $cache_index ]; // Reference the cache element.
/*
* Apply the seed filter.
*
* Return false to prevent the comment or post content from being used.
*
* See WpssoIntegEcomWooCommerce->filter_the_content_seed().
*/
$content = apply_filters( 'wpsso_the_content_seed', '', $mod );
/*
* Make sure that we have a string (empty or not) to work with.
*/
if ( ! is_string( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content from "wpsso_the_content_seed" is ' . gettype( $content ) );
}
$content = '';
} elseif ( '' !== $content ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content seed = ' . $content );
}
} elseif ( $mod[ 'is_post' ] && $mod[ 'id' ] ) {
$content = (string) $mod[ 'wp_obj' ]->post_content;
} elseif ( $mod[ 'is_comment' ] && $mod[ 'id' ] ) {
$content = (string) $mod[ 'wp_obj' ]->comment_content;
}
/*
* Remove singlepics, which we detect and use before-hand.
*/
$count = null;
$content = preg_replace( '/\[singlepic[^\]]+\]/', '', $content, $limit = -1, $count );
if ( $count ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( $count . ' singlepic shortcode(s) removed from content' );
}
}
/*
* Maybe apply 'the_content' filter to expand shortcodes and blocks.
*/
if ( $filter_content ) {
$use_bfo = SucomUtil::get_const( 'WPSSO_CONTENT_BLOCK_FILTER_OUTPUT', true );
$mtime_max = SucomUtil::get_const( 'WPSSO_CONTENT_FILTERS_MAX_TIME', 1.50 );
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'applying filters "the_content"' ); // Begin timer.
}
$content = $this->p->util->safe_apply_filters( array( 'the_content', $content ), $mod, $mtime_max, $use_bfo );
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'applying filters "the_content"' ); // End timer.
}
if ( ! is_string( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content from "the_content" is ' . gettype( $content ) );
}
$content = '';
}
} else {
/*
* Maybe fallback and apply only the 'do_blocks' filters.
*/
if ( $filter_blocks ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'calling do_blocks()' ); // Begin timer.
}
$content = do_blocks( $content );
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'calling do_blocks()' ); // End timer.
}
if ( ! is_string( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content from do_blocks() is ' . gettype( $content ) );
}
$content = '';
}
}
/*
* When the content filter is disabled, fallback and apply our own shortcode filter.
*/
if ( false !== strpos( $content, '[' ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'applying filters "wpsso_do_shortcode"' ); // Begin timer.
}
$content = apply_filters( 'wpsso_do_shortcode', $content );
if ( $this->p->debug->enabled ) {
$this->p->debug->mark( 'applying filters "wpsso_do_shortcode"' ); // End timer.
}
if ( ! is_string( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content from "wpsso_do_shortcode" is ' . gettype( $content ) );
}
$content = '';
}
}
}
/*
* Maybe use only a certain part of the content.
*/
if ( false !== strpos( $content, 'wpsso-content' ) ) {
$content = preg_replace( '/^.*<!-- *wpsso-content *-->(.*)<!--\/wpsso-content *-->.*$/Us', '$1', $content );
}
/*
* Maybe remove text between ignore markers.
*/
if ( false !== strpos( $content, 'wpsso-ignore' ) ) {
$content = preg_replace( '/<!-- *wpsso-ignore *-->.*<!-- *\/wpsso-ignore *-->/Us', ' ', $content );
}
/*
* Remove "Google+" link and text.
*/
if ( false !== strpos( $content, '>Google+<' ) ) {
$content = preg_replace( '/<a +rel="author" +href="" +style="display:none;">Google\+<\/a>/', ' ', $content );
}
/*
* Prefix caption text.
*/
if ( false !== strpos( $content, '<p class="wp-caption-text">' ) ) {
$caption_prefix = $this->p->opt->get_text( 'plugin_p_cap_prefix' );
if ( ! empty( $caption_prefix ) ) {
$content = preg_replace( '/<p class="wp-caption-text">/', '$0' . $caption_prefix . ' ', $content );
}
}
/*
* Apply the filter.
*/
$content = apply_filters( 'wpsso_the_content', $content, $mod );
if ( ! is_string( $content ) ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content from "wpsso_the_content" is ' . gettype( $content ) );
}
$content = '';
}
/*
* Save content to non-persistant cache.
*/
if ( $cache_exp_secs > 0 ) {
/*
* Adds a group or set of groups to the list of non-persistent groups.
*
* Note that only a few caching plugins support this feature.
*/
wp_cache_add_non_persistent_groups( array( __METHOD__ ) );
wp_cache_set( $cache_id, $cache_array, __METHOD__, $cache_exp_secs );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'content array saved to wp_cache for ' . $cache_exp_secs . ' seconds');
}
}
/*
* Maybe put everything on one line but do not cache the re-formatted content.
*/
return $flatten ? preg_replace( '/[\s\r\n]+/s', ' ', $content ) : $content;
}
/*
* Returns the content text, stripped of all HTML tags.
*/
public function get_the_text( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$text = $this->get_the_content( $mod );
$text = preg_replace( '/<!\[CDATA\[.*\]\]>/Us', '', $text );
$text = preg_replace( '/<pre[^>]*>.*<\/pre>/Us', '', $text );
$text = $this->p->util->cleanup_html_tags( $text, $strip_tags = true, $use_img_alt = true );
return apply_filters( 'wpsso_the_text', $text, $mod );
}
/*
* Returns a comma delimited text string of keywords (ie. post tag names).
*/
public function get_keywords_csv( array $mod, $md_key = null ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$keywords = '';
$is_custom = false;
/*
* Check for custom keywords if a metadata index key is provided.
*/
if ( ! empty( $md_key ) && 'none' !== $md_key ) { // $md_key can be a string or array.
if ( ! empty( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { // Just in case.
$keywords = $mod[ 'obj' ]->get_options_multi( $mod[ 'id' ], $md_key );
if ( ! empty( $keywords ) ) {
$is_custom = false;
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'custom keywords = ' . $keywords );
}
}
}
}
/*
* If there's no custom keywords, then go ahead and generate the keywords value.
*/
if ( empty( $keywords ) ) {
$tags = $this->get_tag_names( $mod );
if ( ! empty( $tags ) ) {
$keywords = SucomUtil::array_to_keywords( $tags ); // Returns a comma delimited text string.
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'keywords = ' . $keywords );
}
}
}
return apply_filters( 'wpsso_keywords', $keywords, $mod, $md_key );
}
/*
* Returns a space delimited text string of hashtags.
*/
public function get_hashtags( array $mod, $num_hashtags = false ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$hashtags = apply_filters( 'wpsso_hashtags_seed', '', $mod, $num_hashtags );
if ( ! empty( $hashtags ) ) { // Seed hashtags returned.
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'hashtags seed = ' . $hashtags );
}
} elseif ( false !== $num_hashtags ) {
if ( true === $num_hashtags ) {
$num_hashtags = $this->p->options[ 'og_desc_hashtags' ];
}
if ( is_numeric( $num_hashtags ) && $num_hashtags >= 1 ) {
$tags = $this->get_tag_names( $mod );
$tags = array_slice( $tags, 0, $num_hashtags );
if ( ! empty( $tags ) ) {
$hashtags = SucomUtil::array_to_hashtags( $tags );
if ( $this->p->debug->enabled ) {
$this->p->debug->log( 'hashtags = ' . $hashtags );
}
}
}
}
return apply_filters( 'wpsso_hashtags', $hashtags, $mod, $num_hashtags );
}
/*
* Returns an array of post tags.
*/
public function get_tag_names( array $mod ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
/*
* See WpssoIntegEcomWooCommerce->filter_tag_names_seed().
*/
$tags = apply_filters( 'wpsso_tag_names_seed', null, $mod );
if ( ! is_array( $tags ) ) {
if ( $mod[ 'is_post' ] ) {
if ( 'post' === $mod[ 'post_type' ] ) {
$taxonomy = 'post_tag';
} elseif ( 'page' === $mod[ 'post_type' ] && ! empty( $this->p->options[ 'plugin_page_tags' ] ) ) {
$taxonomy = SucomUtil::get_const( 'WPSSO_PAGE_TAG_TAXONOMY' );
} else $taxonomy = '';
if ( ! empty( $mod[ 'post_type' ] ) && is_string( $mod[ 'post_type' ] ) ) { // Not empty string.
$filter_name = SucomUtil::sanitize_hookname( 'wpsso_' . $mod[ 'post_type' ] . '_tag_taxonomy' );
$taxonomy = apply_filters( $filter_name, $taxonomy, $mod );
if ( ! empty( $taxonomy ) ) {
$tags = wp_get_post_terms( $mod[ 'id' ], $taxonomy, $args = array( 'fields' => 'names' ) );
}
unset( $filter_name, $taxonomy );
}
}
}
$tags = is_array( $tags ) ? array_unique( $tags ) : array();
$tags = apply_filters( 'wpsso_tag_names', $tags, $mod );
if ( $this->p->debug->enabled ) {
$this->p->debug->log_arr( 'tags', $tags );
}
return $tags;
}
/*
* Includes parent names in the term title by default, unless the $title_sep value is false.
*
* See WpssoUtilInline->replace_callback().
* See WpssoFaqShortcodeFaq->do_shortcode().
*/
public function get_term_title( $term_id = 0, $title_sep = null ) {
if ( $this->p->debug->enabled ) {
$this->p->debug->mark();
}
$term_obj = SucomUtilWP::get_term_object( $term_id );
$term_id = isset( $term_obj->term_id ) ? $term_obj->term_id : 0;
$title_text = isset( $term_obj->name ) ? $term_obj->name : '';
$title_sep = $this->maybe_get_title_sep( $title_sep ); // Returns default title separator (decoded) if not provided.
/*
* If we have a title separator and a parent, then redefine the title text with the parent list.
*/
if ( ! empty( $title_sep ) ) {
if ( ! empty( $term_obj->parent ) ) {
$term_parents = get_term_parents_list( $term_id, $term_obj->taxonomy, $args = array(
'format' => 'name', // Use term names or slugs for display.
'separator' => ' ' . $title_sep . ' ', // Separator for between the terms.
'link' => false, // Whether to format as a link.
'inclusive' => true, // Include the term to get the parents for.
) );
if ( $term_parents && ! is_wp_error( $term_parents ) ) {
/*
* Trim excess separator.
*/
$title_text = preg_replace( '/ *' . preg_quote( $title_sep, '/' ) . ' *$/', '', $term_parents );
}
}
}
return apply_filters( 'wpsso_term_title', $title_text, $term_id, $title_sep );
}
/*
* Returns an empty or formatted string (number with minutes).
*/
public function get_reading_time( array $mod ) {
$reading_mins = $this->get_reading_mins( $mod );
return $this->fmt_reading_mins( $reading_mins );
}
public function get_reading_mins( array $mod ) {
$content = $this->get_the_content( $mod );
$words_per_min = WPSSO_READING_WORDS_PER_MIN;
$reading_mins = null;
if ( $mod[ 'obj' ] && $mod[ 'id' ] ) { // Just in case.
$reading_mins = $mod[ 'obj' ]->get_options( $mod[ 'id' ], 'schema_reading_mins' );
}
if ( null === $reading_mins ) { // Default value or no custom value.
$reading_mins = round( str_word_count( wp_strip_all_tags( $content ) ) / $words_per_min );
}
return $reading_mins;
}
public function fmt_reading_mins( $reading_mins ) {
return $reading_mins ? sprintf( _n( '%s minute', '%s minutes', $reading_mins, 'wpsso' ), $reading_mins ) : '';
}
/*
* Add a separator and a value to the left/right hand side of the title.
*/
private function add_title_part( &$title, $title_sep, $part, $hand = 'right' ) {
if ( $part ) { // Adding an empty $part would leave a hanging separator.
if ( $title ) {
if ( 'left' === $hand ) {
$title = $part . ' ' . trim( $title_sep . ' ' . $title );
} else $title = trim( $title . ' ' . $title_sep ) . ' ' . $part;
} else $title = $part; // We have a part, but no title.
}
return trim( $title );
}
/*
* Public method to sanitize arguments or modify values for get_title(), get_description(), etc.
*
* Returns default ellipsis (decoded) if not provided (ie. $ellipsis = null).
*/
private function maybe_get_ellipsis( $ellipsis = null ) {
if ( null === $ellipsis ) {
$ellipsis = html_entity_decode( $this->p->options[ 'og_ellipsis' ], ENT_QUOTES, $this->charset );
}
return $ellipsis;
}
/*
* Public method to sanitize arguments or modify values for get_title(), get_description(), etc.
*
* Returns default title separator (decoded) if not provided (ie. $title_sep = null).
*/
private function maybe_get_title_sep( $title_sep = null ) {
if ( null === $title_sep ) {
$title_sep = html_entity_decode( $this->p->options[ 'og_title_sep' ], ENT_QUOTES, $this->charset );
}
return $title_sep;
}
/*
* Public method to sanitize arguments or modify values for get_title(), get_description(), etc.
*/
private function maybe_get_opt_multi( $mod, $md_key ) {
if ( ! empty( $md_key ) && 'none' !== $md_key ) { // Make sure we have something to work with.
if ( ! empty( $mod[ 'obj' ] ) && $mod[ 'id' ] ) { // Just in case.
return $mod[ 'obj' ]->get_options_multi( $mod[ 'id' ], $md_key );
}
}
return null;
}
/*
* Private method to sanitize arguments or modify values for get_title(), get_description(), etc.
*
* Returns an array of metadata keys (can be empty).
*
* $md_key = true | false | string | array
*/
private function sanitize_md_key( $md_key, $def_key = '' ) {
if ( false === $md_key || 'none' === $md_key || '' === $md_key ) { // Nothing to do.
return array();
} elseif ( true === $md_key || null === $md_key || $md_key === $def_key ) { // Return the default key array.
return WpssoConfig::get_md_keys_fallback( $def_key );
}
if ( ! is_array( $md_key ) ) {
$md_key = WpssoConfig::get_md_keys_fallback( $md_key );
}
foreach ( WpssoConfig::get_md_keys_fallback( $def_key ) as $key ) {
$md_key[] = $key;
}
$md_key = array_unique( $md_key ); // Just in case.
return $md_key;
}
/*
* Private method to sanitize arguments or modify values for get_title(), get_description(), etc.
*
* Return 0 by default.
*/
private function sanitize_max_len( $max_len ) {
if ( is_numeric( $max_len ) ) {
return (int) $max_len;
} elseif ( is_array( $max_len ) && isset( $max_len[ 'max' ] ) ) {
return (int) $max_len[ 'max' ];
} elseif ( is_string( $max_len ) ) {
$input_limits = WpssoConfig::get_input_limits( $max_len ); // Uses a local cache.
if ( ! empty( $input_limits[ 'max' ] ) ) {
return (int) $input_limits[ 'max' ];
}
}
return 0;
}
}
}